JavaScript
是面对对象编程,可是它又跟其余编程语言不同,不一样之处是JavaScript
面对对象并非依赖于抽象类,而是经过原型链。在C++
和Java
使用new
命令时,都会调用"类"的构造函数。而在Javascript
语言中,new
命令后面跟的不是类,而是构造函数。可是,用构造函数生成实例对象,有一个缺点,那就是没法共享属性和方法。为了解决这个问题,因而在构造函数设置了prototype
属性,也就是原型。前端
用构造函数生成实例对象,有一个缺点,那就是没法共享属性和方法。从而形成对系统资源的浪费。具体例子以下:es6
function Cat(name, color) {
this.name = name;
this.color = color;
this.meow = function () {
console.log('喵喵');
};
}
var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');
cat1.meow === cat2.meow
// false
复制代码
从上面的代码,能够cat1
和cat2
的meow
方法是独立的可是实现的功能都是同样的,这既没有必要,又浪费系统资源,由于全部meow
方法都是一样的行为,彻底应该共享。这个问题的解决方法,就是JavaScript
构造函数中添加prototype
属性。编程
只有函数才有prototype属性,又称为显式原型。
prototype属性包含一个对象,全部实例对象须要共享的属性和方法,都放在这个对象里面;那些不须要共享的属性和方法,就放在构造函数里面。浏览器
JavaScript
规定,每一个函数都有一个prototype
属性,指向一个对象。bash
function f() {}
typeof f.prototype // "object"
复制代码
对于普通函数来讲,该属性基本无用。可是,对于构造函数来讲,生成实例的时候,该属性会自动成为实例对象的原型。前端工程师
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
复制代码
上面代码中,构造函数Animal
的prototype
属性,就是实例对象cat1
和cat2
的原型对象。原型对象上添加一个color
属性,结果,实例对象都共享了该属性。编程语言
原型对象的属性不是实例对象自身的属性。只要修改原型对象,变更就马上体如今全部实例对象上函数
Animal.prototype.color = 'yellow';
cat1.color // "yellow"
cat2.color // "yellow"
复制代码
若是实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。学习
cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'
Animal.prototype.color // 'yellow';
复制代码
总结一下,原型对象的做用,就是定义全部实例对象共享的属性和方法。这也是它被称为原型对象的缘由,而实例对象能够视做从原型对象衍生出来的子对象。ui
注:Object.prototype中Object是一个全局对象,也是一个构造函数,以及其余基本类型的全局对象也都是构造函数。
_proto_
是每一个对象都有的属性,又称为隐式原型。可是,_proto_
不是一个规范属性,只是部分浏览器实现了此属性,对应的标准属性是[[Peototype]]
。
大多状况下,
_proto_
能够理解为‘构造函数的原型’,是实例和它的构造函数之间创建的连接,即_proto_ ===constructor.prototype
例子:
// 字面量方式
var a = {};
console.log(a.prototype); //undefined
console.log(a.__proto__); //Object {}
//构造器方式
var b = function(){}
console.log(b.prototype); //b {}
console.log(b.__proto__); //function() {}
// Object.create方式
var a1 = {}
var a2 = Object.create(a1)
console.log(b.prototype); //undefined
console.log(b.__proto__); //Object a1
复制代码
本段摘自阮一峰ES6入门,具体解析能够点击查看。
一、_proto_
属性没有写入ES6
的正文,而是写入了附录,
二、缘由是__proto__
先后的双下划线,说明它本质上是一个内部属性,而不是一个正式的对外的API
,只是因为浏览器普遍支持,才被加入了ES6
。
三、标准明确规定,只有浏览器必须部署这个属性,其余运行环境不必定须要部署,并且新的代码最好认为这个属性是不存在的。
四、不管从语义的角度,仍是从兼容性的角度,都不要使用这个属性,而是使用下面的Object.setPrototypeOf()
(写操做)、Object.getPrototypeOf()
(读操做)、Object.create()
(生成操做)代替。
每一个对象都会在其内部初始化一个属性,就是prototype
(原型),当咱们访问一个对象的属性时,若是这个对象内部不存在这个属性,那么他就会去prototype
里找这个属性,这个prototype
又会有本身的prototype
,因而就这样一直找下去,也就是咱们平时所说的原型链的概念。
全部对象的原型最终均可以上溯到Object.prototype
,Object.prototype
对象有没有它的原型呢?回答是Object.prototype
的原型是null
。null
没有任何属性和方法,也没有本身的原型。所以,原型链的尽头就是null
。
一、字符串原型链终点
let test = 'hello'
// 字符串原型
let stringPrototype = Object.getPrototype(test)
// 字符串的原型是String对象
stringPrototype === String.Prototype // true
// String对象的原型是Object对象
Object.getPrototypeOf(stringPrototype) === Object.prototype // true
复制代码
二、函数原理链的终点
let test = function () {}
let fnPrototype = Object.getPrototype(test)
fnPrototype === Function.prototype // true
Object.getPrototypeOf(Function.prototype) === Object.Prototype // true
复制代码
一、属性查找
当JS引擎查找对象的属性时,先查找对象自己是否存在该属性,若是不存在,会去原型链上查找,但不会查找本自身的prototype。
用一个例子来形象说明一下:
let test = 'hello'
// 字符串原型
let stringPrototype = Object.getPrototype(test)
// 字符串的原型是String对象
stringPrototype === String.Prototype // true
// String对象的原型是Object对象
Object.getPrototypeOf(stringPrototype) === Object.prototype // true
复制代码
二、拒绝查找原型链
hasOwnPrototype
:指示对象自身属性中是否具备指定的属性。
语法:obj.hasOwnPrototype(prop)
。
参数prop
:查找的属性。
返回值:Boolean
判断是否存在。
let test = {'ABC': 'EDF' }
test.hasOwnPrototype('ABC') // true
test.hasOwnPrototype('toString') // false
复制代码
该方法是在Object.prototype
上,全部对象均可以使用,且会忽略掉那些从原型链上继承的属性。
prototype
对象有一个constructor
属性,默认指向prototype
对象所在的构造函数。
function P() {}
P.prototype.constructor === P // true
复制代码
因为constructor
属性定义在prototype
对象上面,意味着能够被全部实例对象继承。
function P() {}
var p = new P();
p.constructor === P // true
p.constructor === P.prototype.constructor // true
p.hasOwnProperty('constructor') // false
复制代码
上面代码中,p
是构造函数P
的实例对象,可是p
自身没有constructor
属性,该属性实际上是读取原型链上面的P.prototype.constructor
属性。
constructor
属性的做用是,能够得知某个实例对象,究竟是哪个构造函数产生的。
举个小栗子:
function F() {};
var f = new F();
f.constructor === F // true
f.constructor === RegExp // false
复制代码
另外一方面,有了constructor
属性,就能够从一个实例对象新建另外一个实例。
我再举个小栗子:
function Constr() {}
var x = new Constr();
var y = new x.constructor();
y instanceof Constr // true
复制代码
天下无难事,只怕有心人。原型和原型链算是前端工程师的开发基础了,原型虽然不太好理解,可是我相信只要有心学习和实践,也没有想象中的那么难。但愿,这篇文章对我本身帮组的同时,童鞋们看完以后也有必定的收获。