天天看點

javascript 的 繼承(六) 之 寄生組合式繼承

前面說過,組合繼承是JavaScript 最常用的繼承模式;不過,它也有自己的不足。

組合繼承最大的問題就是無論什麼情況下,都會調用兩次超類型構造函數:一次是在建立子類型原型的時候,另一次是在子類型構造函數内部。

沒錯,子類型最終會包含超類型對象的全部執行個體屬性,但我們不得不在調用子類型構造函數時重寫這些屬性。

再來看一看下面組合繼承的例子。

function SuperType(name){
     this.name = name;
     this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
     alert(this.name);
};
function SubType(name, age){
     SuperType.call(this, name); //第二次調用SuperType()
     this.age = age;
}
SubType.prototype = new SuperType(); //第一次調用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
};
           

在第一次調用SuperType 構造函數時,SubType.prototype 會得到兩個屬性:name 和colors;

它們都是SuperType 的執行個體屬性,隻不過現在位于SubType 的原型中。

當調用SubType 構造函數時,又會調用一次SuperType 構造函數,這一次又在新對象上建立了執行個體屬性name 和colors。

于是,這兩個屬性就屏蔽了原型中的兩個同名屬性。下圖 展示了上述過程。

javascript 的 繼承(六) 之 寄生組合式繼承

如圖所示,有兩組name 和colors 屬性:一組在執行個體上,一組在SubType 原型中。

這就是調用兩次SuperType 構造函數的結果。

好在我們已經找到了解決這個問題方法——寄生組合式繼承。

所謂寄生組合式繼承,即通過借用構造函數來繼承屬性,通過原型鍊的混成形式來繼承方法。

其背後的基本思路是:不必為了指定子類型的原型而調用超類型的構造函數,我們所需要的無非就是超類型原型的一個副本而已。

本質上,就是使用寄生式繼承來繼承超類型的原型,然後再将結果指定給子類型的原型。

寄生組合式繼承的基本模式如下所示。

function inheritPrototype(subType, superType){
     var prototype = object(superType.prototype); //建立對象
     prototype.constructor = subType; //增強對象
     subType.prototype = prototype; //指定對象
}
           

這個示例中的inheritPrototype()函數實作了寄生組合式繼承的最簡單形式。

這個函數接收兩個參數:子類型構造函數和超類型構造函數。

在函數内部,第一步是建立超類型原型的一個副本。第二步是為建立的副本添加constructor 屬性,進而彌補因重寫原型而失去的預設的constructor 屬性。

最後一步,将新建立的對象(即副本)指派給子類型的原型。

這樣,我們就可以用調用inheritPrototype()函數的語句,去替換前面例子中為子類型原型指派的語句了,例如:

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    SuperType.call(this, name);
    this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
    alert(this.age);
};
           

這個例子的高效率展現在它隻調用了一次SuperType 構造函數,并且是以避免了在SubType.prototype 上面建立不必要的、多餘的屬性。

與此同時,原型鍊還能保持不變;是以,還能夠正常使用instanceof 和isPrototypeOf()。

開發人員普遍認為寄生組合式繼承是引用類型最理想的繼承範式。

繼續閱讀