13 對象 - 繼承
許多 OO 語言都支持兩種繼承方式:接口繼承和實現繼承。接口繼承只繼承方法簽名,而實現繼承則繼承實際的方法。
由於JS函數沒有簽名,在 ECMAScript 中無法實現接口繼承。 ECMAScript 只支持實現繼承,而且其實現繼承主要是依靠原型鏈來實現的。
1 繼承的原理
繼承是通過創建 SuperType 的實例,並將該實例賦給SubType.prototype 實現的。
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ returnthis.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
關系圖如下:
拿上面的例子來說,調用instance.getSuperValue()會經歷三個搜索步驟:
1)搜索實例;
2)搜索 SubType.prototype;
3)搜索 SuperType.prototype,最後一步才會找到該方法。
在找不到屬性或方法的情況下,搜索過程總是要一環一環地前行到原型鏈末端才會停下來。
事實上,前面例子中展示的原型鏈還少一環。我們知道,所有引用類型默認都繼承了 Object,而這個繼承也是通過原型鏈實現的。
所以,完整的原型鏈應該如下:
由於原型鏈的關系,我們可以說 instance 是 Object、 SuperType 或 SubType 中任何一個類型的實例。
原型鏈雖然很強大,可以用它來實現繼承,但它也存在一些問題。
主要是由於共享導致的。如下代碼:
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ } //繼承了 SuperType SubType.prototype = new SuperType(); var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green,black"
因為這種缺陷,實踐中很少會單獨使用原型鏈。
2 借用構造函數(經典繼承)
function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ //繼承了 SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"
解讀:
第一, new SubType()會創建一個新對象。
第二, SubType()中的this指向這個新對象。
第三, 在這個新對象中執行SuperType()函數,也相當於在這個新對象中運行SuperType()函數,也就給這個新對象加了一個屬性colors數組。
第四, 將這個新對象名為為instance1。
這種方法的缺陷:方法都在構造函數中定義,因此函數復用就無從談起了。考慮到這些問題,借用構造函數的技術也是很少單獨使用的。
3 組合繼承
指的是將原型鏈和借用構造函數的技術組合到一塊,從而發揮二者之長的一種繼承模式。
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; } //繼承方法 SubType.prototype = new SuperType(); SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優點,成為 JavaScript 中最常用的繼承模式。
13 對象 - 繼承