javascript
《JavaScript 高级程序设计》学习总结六(3)
引言:繼承是面向對象語言中的一個最為人津津樂道的概念。許多面向對象都支持兩種繼承方式:接口繼承與實現繼承,接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。如前所述,由于JavaScript 中函數沒有簽名,所以在ECMAScript 中無法實現接口繼承。ECMAScript 只支持實現繼承,而實現繼承主要依靠原型鏈來實現的。
?
原型鏈:
概念:每個構造函數都要一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的指針,那么假如我們讓原型對象等于另一個類型的實例結果會怎么樣呢?顯然,此時的原型對象將包含一個指向另一個原型的指針,相應的,另一個原型中也包含著一個指向另一個構造函數的指針。假如另一個原型又是另一個類型的實例,那么上述關系依然成立,如此層層遞進,就構成了實例與原型的鏈條。
說的太抽象了,我們直接看案例:實現原型鏈有一種基本模式:
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 SubType();alert(instance.getSuperValue()); //true?
如圖:
我們沒有使用 SubType 默認提供的原型,而是給它換了一個新原型;這個新原型 就是 SuperType 的實例。于是,新原型不僅具有作為一個 SuperType 的實例所擁有的全部屬性和方法, 而且其內部還有一個指針,指向了 SuperType 的原型。最終結果就是這樣的:instance 指向 SubType 的原型, SubType 的原型又指向 SuperType 的原型。 getSuperValue() 方法仍然還在 SuperType.prototype 中,但 property 則位于 SubType.prototype 中。這是因為 property 是一 個實例屬性,而 getSuperValue()則是一個原型方法。既然 SubType.prototype 現在是 SuperType的實例,那么 property 當然就位于該實例中了。此外,要注意 instance.constructor 現在指向的 是 SuperType,這是因為原來 SubType.prototype 中的 constructor 被重寫了的緣故(① 實際上,不是 SubType 的原型的 constructor 屬性被重寫了,而是 SubType 的原型指向了另一個對象—— SuperType 的原型(?SubType.prototype = new SuperType()),而這個原型對象的 constructor 屬性指向的是 SuperType。)。
?
別忘記默認的原型:
我們知道,所有引用類型默認都繼承了 Object,而 這個繼承也是通過原型鏈實現的。大家要記住,所有函數的默認原型都是 Object 的實例,因此默認原型都會包含一個內部指針,指向 Object.prototype。這也正是所有自定義類型都會繼承 toString()、 valueOf()等默認方法的根本原因。所以,我們說上面例子展示的原型鏈中還應該包括另外一個繼承層 次。
如圖:
?
一句話,SubType 繼承了 SuperType,而 SuperType 繼承了 Object。當調用 instance.toString() 時,實際上調用的是保存在 Object.prototype 中的那個方法。
?
確定原型與實例的關系:
?確定原型和實例的關系 可以通過兩種方式來確定原型和實例之間的關系。第一種方式是使用 instanceof 操作符。
第二種:是使用 isPrototypeOf()方法。同樣,只要是原型鏈中出現過的原型,都可以說是該 原型鏈所派生的實例的原型,因此 isPrototypeOf()方法也會返回 true
舉個例子:
alert(Object.prototype.isPrototypeOf(instance)); //true
?
謹慎地定義方法:
子類型有時候需要重寫超類型中的某個方法,或者需要添加超類型中不存在的某個方法。但不管怎 樣,給原型添加方法的代碼一定要放在替換原型的語句之后。來看下面的例子
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 false;};var instance = new SubType();alert(instance.getSuperValue()); //false
?
加粗的部分是兩個方法的定義。第一個方法 getSubValue()被添加到了 SubType 中。第二個方法 getSuperValue()是原型鏈中已經存在的一個方法,但重寫這個方法將會屏蔽原來的 那個方法。換句話說,當通過 SubType 的實例調用 getSuperValue()時,調用的就是這個重新定義 的方法;但通過 SuperType 的實例調用 getSuperValue()時,還會繼續調用原來的那個方法。這里 要格外注意的是,必須在用 SuperType 的實例替換原型之后,再定義這兩個方法(也就是上面的://繼承了 SuperType?SubType.prototype = new SuperType(); )
?
PS :值得注意的是:通過原型鏈實現繼承時2,不能用對象字面量創建原型方法,因為這樣會重寫原型鏈。比如我們將上面的代碼改成
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!以上代碼展示了剛剛把 SuperType 的實例賦值給原型,緊接著又將原型替換成一個對象字面量而 導致的問題。由于現在的原型包含的是一個 Object 的實例,而非 SuperType 的實例,因此我們設想 中的原型鏈已經被切斷——SubType 和 SuperType 之間已經沒有關系了。
?
原型鏈的問題
上一章節我們提到,原型模式的自身問題,那就是原型可以實現屬性共享,而這也正是為什么要在構造函數中,而不是在原型對象中定義屬性的原因。在通過原型來實現繼承時,原型實際上會變成另一個類型的實例。于是,原先的實例屬性也就順理成章地變成了現在的原型屬性了。舉個例子:
1 function SuperType(){ this.colors = ["red", "blue", "green"]; 2 3 } 4 5 function SubType(){ } 6 7 //繼承了 8 9 SuperType SubType.prototype = new SuperType(); 10 11 var instance1 = new SubType(); 12 13 instance1.colors.push("black"); 14 15 alert(instance1.colors); //"red,blue,green,black" 16 17 var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green,black"這個例子中的 SuperType 構造函數定義了一個 colors 屬性,該屬性包含一個數組(引用類型值)。 SuperType 的每個實例都會有各自包含自己數組的 colors 屬性。當 SubType 通過原型鏈繼承了 SuperType 之后,SubType.prototype 就變成了 SuperType 的一個實例,因此它也擁有了一個它自 己的 colors 屬性——就跟專門創建了一個 SubType.prototype.colors 屬性一樣。但結果是什么 呢?結果是 SubType 的所有實例都會共享這一個 colors 屬性。而我們對 instance1.colors 的修改 能夠通過 instance2.colors 反映出來,就已經充分證實了這一點。
原型鏈的第二個問題是:在創建子類型的實例時,不能向超類型的構造函數中傳遞參數。實際上, 應該說是沒有辦法在不影響所有對象實例的情況下,給超類型的構造函數傳遞參數。有鑒于此,再加上 前面剛剛討論過的由于原型中包含引用類型值所帶來的問題,實踐中很少會單獨使用原型鏈。
?
----------------------------------------------------------------------本章節完------------------------------------------------------------
下一章節預告:本章節我們總結學習了繼承,同時也了解到原型鏈繼承機制碰到的問題,那么這個問題我們會在下一章節進行學習總結。
(JavaScript的繼承是這個語言的重點部分,我在寫這篇博文時一直在想怎么總結才好,要不要自己用自己的理解與語言重寫,糾結了好一會后自己寫了一篇,但是不如人意,決定還是使用書中的文字,以后隨著深入,再重開一篇單獨講繼承。還有一個比較尷尬,我現在才發現博客的“插入代碼”功能可以幫助更好的排版,所以我又得重新將前面的博文重新排版了)
?
轉載于:https://www.cnblogs.com/wxhhts/p/9479926.html
總結
以上是生活随笔為你收集整理的《JavaScript 高级程序设计》学习总结六(3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity3d XmlException
- 下一篇: Puppet的一些奇技淫巧