1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | function SuperType(){ this .property= true ; } SuperType.prototype.getSuperValue= function (){ return this .property; } function SubType(){ this .subproperty= false ; } //繼承了SuperType SubType.prototype= new SuperType (); SubType.prototype.getSubValue= function (){ return this .subproperty; } var instance= new Son(); alert(instance.getSuperValue()); //true alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true |
以上程式碼定義了兩個型別:SuperType和SubType。每個型別分別有一個屬性和方法。它們主要區別就是SubType繼承了SuperType,而這繼承是通過建立SuperType新例項,並將這個新例項賦給SubType.prototype實現的。實現的本質就是重寫原型物件,代之以新型別的例項。
instance指向Subtyoe.prototype,SubType.prototype又指向了SuperType.prototype。getSuperValue()方法仍然還在SuperType。prototype中,但property則位於SubType.prototype中。這是因為property是一個例項屬性,而getSyoerValue()則是一個原型方法。既然SubType.prototype現在是SuperType的例項,那麼property當然位於該例項中。此外,要注意instance.constrcutor現在指向的是SuperType,這是因為原來SubType.propertype中的constructor被重寫了的緣故。
注:SubType.propertype中的constructor不被重寫了的緣故,而是SubType的原型指向了另一個物件——SuperType的原型,而這個原型物件的constructor是指向SuperType的。
通過實現原型鏈,本質上就是擴充套件了原型搜尋機制。
2.別忘了預設的原型
事實上,我們的原型鏈還少了一環,所有引用型別預設繼承了Object型別,而這個繼承也是通過原型鏈實現的。我們要記住,所有函式的預設原型都是Object的例項,因此預設原型都會包含一個內部指標,指向Object.prototype。這也正式自定義型別能使用toString()等預設方法的原因。
一句話,SubType整合了SuperType,而SuperType繼承了Object。當呼叫instance.toSring()時,實際呼叫的是儲存在Object.prototype中的那個方法。
3.確定原型和例項的關係
可以使用instanceof操作符來測試例項與原型鏈中出點過的建構函式。
1 2 3 | alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true |
4.謹慎的定義方法
子型別有時需要重寫父型別中的某個方法,或者需要新增父型別中不穿在的某個方法,但不管怎麼,給原型新增的程式碼一定要放在替換原型的語句之後。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function SuperType(){ this .property= true ; } SuperType.prototype.getSuperValue= function (){ return this .property; } function SubType(){ this .subproperty= false ; } //繼承了SuperType SubType.prototype= new SuperType(); //新增新方法 SubType.prototype.getSubValue= function (){ return this .subproperty; } //重寫父型別中的方法 SubType.prototype.getSuperValue= function (){ return this .subproperty; } var instance= new SubType(); alert(instance.getSuperValue()); //false |
注意,在通過原型鏈實現繼承時,不能使用物件字面量建立原型物件,這樣會重寫原型鏈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function SuperType(){ this .property= true ; } SuperType.prototype.getSuperValue= function (){ return this .property; } function SubType(){ this .subproperty= false ; } //繼承了SuperType SubType.prototype= new SuperType(); //使用字面量新增新方法,會導致上一行程式碼無效 SubType.prototype={ getSubValue: function (){ return this .subproperty; }, someOtherMethod: function (){ return false ; } } var instance= new SubType(); alert(instance.getSuperValue()); //error |
由於現在的原型包含的是一個Object的例項,而非SuperType的例項,因此我們設想中的原型鏈已經別切斷——SubType和SuperType已經沒關係了。
5.原型鏈的問題
原型鏈最主要的問題還是來支援引用型別的原型,我們以前說過包含引用型別值的原型屬性會被所有例項共享,這也是為什麼要在建構函式中定義屬性,而不是在原型中定義屬性的原因了。在通過原型來實現繼承時,原型實際上會被變成另一個型別的例項。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function SuperType(){ this .colors=[ "red" , "blue" , "green" ]; } function SubType(){ } //繼承了SuperType SubType.prototype= new SuperType(); var instance= new SubType(); instance.colors.push( "black" ); //"red","blue","green","black" alert(instance.colors); var instance2= new SubType(); alert(instance2.colors); //"red","blue","green","black" |
原型的第二個問題:在建立子類的例項時,不能向父型別的建構函式草地引數,所以實踐中很少單獨用到原型鏈。