原型对象
所有的函数都会有一个prototype属性,这个属性是公有且不可枚举的。它会指向一个对象,而这个对象则被称为该函数的原型对象:
function Foo() {
// ...
}
Foo.prototype; // {}
在默认的情况下,所有的原型对象都有一个constructor
属性,这个属性包含一个指向prototype
属性所在函数的指针。拿之前的例子来讲,Foo.prototype.constructor
指向Foo
。创建了自定义函数后,其原型对象默认只有一个constructor
属性,而其他的方法都是从Object
继承而来,当把该函数当作构造函数进行实例化的时候(使用new
操作符),创建的实例内部包含一个指针[[Prototype]]
,指向构造函数的原型对象。
function Person(name) {
this.name = name;
}
var person1 = new Person('cherry');
var person2 = new Person('Nick');
person1.name; // cherry
person2.name; // Nick
下图展示了各个对象之间的关系:
这个图展示了Person
和Person的原型对象
和Person
的两个实例之间的关系,从图中可以看出,Person.prototype
属性指向Person的原型对象
,而Person的原型对象
的constructor
属性指向Person函数
,Person
的每个实例person1
和person2
内部都有一个[[Prototype]]属性
,指向Person的原型对象
。
原型链
什么是原型链
回顾一下上面描述的构造函数,原型和实例之间的关系。每个构造函数都有一个原型对象,而每个实例中又包含一个指向原型对象的指针,如果我们将原型对象等于另外一个构造函数的实例,那么这个原型对象就会包含一个指向另一个原型对象的指针,这样层层递进,就构成了实例和原型之间的链条,而这个链条就是原型链。
原型链的作用
我们知道在JavaScript中,在引用对象的属性时,会触发对象的[[Get]]操作
,对于默认的[[Get]]操作来说,第一步就是检测对象本身是否有该属性,如果有就引用它。但如果该属性不在对象本身中,此时就需要检查[[Prototype]]链
了。
var anotherObject = { a:2 };
// 创建一个关联到 anotherObject 的对象
var myObject = Object.create( anotherObject );
myObject.a; // 2
Object.create(…) 会创建一个 对象并把这个对象的 [[Prototype]] 关联到指定的对象
但是如果anotherObject
中也没有a
属性的并且[[Prototype]]链
不为空,这个查找过程就会顺着[[Prototype]]链
继续下去。
这个过程会持续到找到匹配的属性名或者查找完整条[[Prototype]] 链
。如果是后者的话,[[Get]]
操作的返回值是undefined
。
原型链的尽头
所有普通的[[Prototype]]链
的尽头都是Object.prototype