概述
首先說明,學習原型鏈的知識是爲了更好的理解原型和原型對象,在實際應用中很少單獨使用下面介紹的原型鏈,具體的原因是下面提到的它的缺點。
我們複習一下之前的知識:
- 構造函數的prototype指向了原型對象
- 原型對象中constructor指向了構造函數
- 實例中的__proto__指向了原型對象
這時候我們如果將一個實例的原型指針,指向另一個對象的實例,這時候這些實例就會串成一個鏈條,即A實例的__proto__指向B實例,B實例的__proto__又指向其原型對象。下面的代碼是原型鏈的基本實現:
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType(); //將SubType的原型對象指向SuperType的實例
SubType.prototype.getSubValue = function() {
return this.subProperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); // true
其繼承關係如下圖
注意,SubType的prototype指向SuperType的一個實例,所以SubType的prototype的prototype就指向了SuperType的prototype。(理解清楚,有點繞口)
爲了強化這個概念,我們看一個問題,在上面的代碼中,instance的constructor屬性指向誰呢?答案是指向SuperType的構造函數。爲什麼呢?我們來分析一下:
- 在原型鏈中SubType的prototype指向SuperType的一個實例
- 實例並沒有constructor屬性,這個屬性是在prototype中的,原型對象中的這個屬性指向了構造函數
- instance沒有constructor,要從原型對象中獲得constructor
- 其原型對象是SuperType的一個實例,這個實例也沒有constructor,要從自己的prototype中獲得
- 從SuperType的一個實例的prototype中獲得的constructor就是SuperType的構造函數
默認原型Object
我們之前說過,如果寫了一個構造函數,就會創建一個默認的原型對象,現在我們學習了原型鏈之後我們就能進一步完整這個理解。
創建的默認的原型對象的prototype指向Object,從Object中繼承了toString ValueOf等方法。所以完整的原型鏈如下如:
注意SuperType的prototype指向Object,而SubType的prototype原本也指向Object,我們將其指向了Supertype的實例,實現了原型鏈。
注意在上面的圖中,SubType Prototype其實就是SuperType的一個實例,所以它的Prototype才指向了SuperType的Prototype,同理,Supertype Prototype也是Object的一個實例。
對象識別
- instanceof 這個操作符可以判斷實例的任意一個父構造函數
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true
- isPrototypeOf 判斷實例的原型,任意在鏈上的原型都可以
alert(Object.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true
複寫方法
注意複寫方法一定要在替換原型對象之後,這個也很好理解,因爲我們在替換了原型對象之後,指針就改變了。
原型鏈的問題
- 實例不復寫直接使用原型中的引用類型屬性,這個屬性會在所有的實例中共享。這是我們不希望看到的。
function SuperType() { this.property = ['red']; } function SubType() {} SubType.prototype = new SuperType(); //將SubType的原型對象指向SuperType的實例 var instance = new SubType(); instance.property.push('blue'); var ins2 = new SubType(); alert(ins2.property); //red,blue 無法各自擁有自己的,原先屬於原型對象的引用類型
- 創建子類型的時候不能向超類型的構造函數中傳遞參數