您好,登錄后才能下訂單哦!
JavaScript中的繼承模式有哪些,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
一. 原型鏈繼承
所謂原型鏈繼承,就是讓父類的一個實例作為子類的原型。
即 :
parentInstance = new Parent(); child.prototype = parentInstance;
這樣,在創建子類的實例時,子類實例的__proto__指向父類的實例(即此時子類構造函數的prototype屬性),而父類實例的__proto__又指向父類構造函數的prototype屬性。借用這種方式形成了一條原型鏈。
由于JavaScript中搜索實例中調用的變量有如下方式:
在當前實例中尋找變量名
在當前實例所指向的原型中尋找
假設原型鏈中有如下繼承關系:
grandparent(有方法 grandparent.prototype.sayHello) -> parent -> child
當在child的實例child_ming調用方法 sayHello 時,首先在child_ming中(即只定義在child_ming這一個實例中,而非所有實例中)搜索sayHello,并未找到,然后開始搜索它所指向的原型,即parent的實例。在parent的實例中也沒有此方法,開始搜索parent的原型,即grandparent的實例。在grandparent的實例中依然沒有找到,又搜索grandparent的原型并找到該方法。
可以看出,這樣便實現了繼承。
如同在使用prototype創建對象時遇到的問題,倘若完全使用原型鏈進行繼承,會使得一些需要繼承但不需要在不同實例間進行共享的屬性變得不方便實現。
下面就要說一說借用構造函數實現的繼承。
二. 借用構造函數實現繼承
所謂借用構造函數實現繼承,即在子類的構造函數中把父類的構造函數借來使用,以求在子類中生成父類的屬性。
看如下代碼:
function Parent(color){ this.color = color; this.getColor = function(){ alert(this.color); }; } function Child(color,size){ Parent.call(this,color); this.size = size; }
這就是一個簡單的借用構造函數的繼承。
通過使用父類的構造函數,可以使得同一構造函數的不同的實例的同一屬性擁有不同的值。解決了原型鏈繼承中的屬性共享的弊端。
然而,如同使用構造函數創建對象時遇到的問題,通過構造函數生成的方法面臨著重復定義的問題,同一類下的不同實例擁有各自的方法,而一般來講方法是需要實現復用的,沒有必要讓它們擁有各自的方法。
使用組合繼承可以解決這個問題。
三. 組合繼承(原型鏈與借用構造函數)
既然原型鏈可以實現屬性共享,借用構造函數可以實現屬性值的私有,不妨將它們結合起來,這就形成了組合繼承。
所謂組合繼承,實際上就是用一次原型鏈繼承,用一次借用構造函數繼承。
看如下代碼:
function Parent(color){ this.color = color; } Parent.prototype.getColor = function(){ alert(this.color); }; function Child(color,size){ Parent.call(this,color); //借用構造函數繼承屬性 this.size = size; } //下面的屬性'green'并沒有影響,這里要使用原型繼承方法 Child.prototype = new Parent('green'); var child_demo = new Child('red',200);
首先思考這樣兩個問題,創建一個child 實例到底生成了幾份color 屬性?在代碼中既定義了'red' 又定義了 ‘green',當調用 child_demo.getColor()
時,到底會alert 哪一個?
首先來看第一個問題。由于子類構造函數中借用了父類的構造函數,在創建子類實例時必然會生成一次color 屬性。但是不要忘記,我們在繼承方法時是讓子類構造函數的原型指向一個父類的實例,在創建這個父類實例時還要生成一次color 屬性(即上面'green'處),而這個屬性是完全沒有必要存在的。所以一共生成了兩份color 屬性,一個有用一個沒用。
再來看第二個問題。只要能理解this 的含義就可以知道:
child_demo.getColor() // 'red' Child.prototype.getColor() //'green'
組合繼承結合了前兩種繼承方式的優點,但它也有自己的缺點。從生成兩份color 屬性可以知道在繼承過程中調用了兩次Parent 的構造函數,這會造成執行完成速度的問題,影響了效率。但是瑕不掩瑜,這種繼承方式還是成為了JavaScript中最常用的繼承模式。
四. 原型式繼承
原型式繼承是從已有對象的基礎上繼承,基于已有對象創建新的對象。
看如下代碼:
var obj = { color: 'red', getColor: function(){ alert(this.color); }, }; //getChild(obj)返回的是一個__proto__指向obj的實例 function getChild(obj){ function func(){} func.prototype = obj; return new func(); } var child_demo = getChild(obj);
這種繼承方式與原型鏈式的繼承方式有相同點和不同點。
相同點:它們都是通過改變子類構造函數的原型屬性來實現繼承,所繼承的屬性都具有不同實例共享的特點。
不同點:原型鏈繼承中子類構造函數的原型(prototype)是父類的一個實例(我們真正需要繼承的東西可能存在于父類構造函數的原型中,也可能存在于直接指向的父類實例中),而原型式繼承中子類構造函數的原型是一個已有的對象,可以說直接就是父類。
五. 寄生式繼承
寄生式繼承可以說是原型式繼承的變體,它對原型式繼承進行了封裝,使得創建子類實例只依賴于一個函數。
看如下代碼:
var obj = { color: 'red', getColor: function(){ alert(this.color); }, }; function getChild(obj){ function func(){} func.prototype = obj; return new func(); } /*-------以上是原型式繼承的代碼----------*/ function betterGetChild(obj,size){ var temp_obj = getChild(obj); temp_obj.size = size; temp_obj.getSize = function(){ return this.size; }; return temp_obj; } var demo = betterGetChild(obj,200);
六. 寄生組合式繼承
寄生組合式是組合繼承加上原型式繼承的應用。由于組合式繼承中子類構造函數的原型指向父類的一個實例而非父類構造函數的原型,會導致設置子類構造函數的原型時對父類的構造函數進行一次額外的調用。所以在寄生式組合繼承中,借用原型式繼承的思想,將父類構造函數的原型當作一個已有對象,讓子類構造函數的原型直接指向它。
看如下代碼:
function getChild(obj){ function func(){} func.prototype = obj; return new func(); //調用構造函數 } /*-------以上是原型式繼承的代碼----------*/ function Parent(color){ this.color = color; } Parent.prototype.getColor = function(){ alert(this.color); }; function Child(color,size){ Parent.call(this,color); //調用構造函數 this.size = size; } //Child.prototype直接指向一個__proto__指向Parent.prototype的實例 Child.prototype = getChild(Parent.prototype); Child.prototype.getSize = function(){ alert(this.size); };
其實嚴格來說,這種方式也調用了兩次構造函數,但是其中一次構造函數的調用是對一個空函數的調用,而不是兩次都調用父類的構造函數。
在組合繼承中,在子類的原型中其實還保存有父類的沒有定義在原型中的屬性(由于子類構造函數的原型是父類的一個實例),只不過是子類構造函數在借用父類構造函數在當前實例中生成了覆蓋原型中那些屬性的屬性。而寄生式組合繼承中,根本不會產生那些冗余數據。
人們普遍認為寄生時組合繼承是最理想的繼承方式。
看完上述內容,你們掌握JavaScript中的繼承模式有哪些的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。