原型對象
所有的函數都會有一個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