許多OO語言都支持兩種繼承方式:接口繼承和實(shí)現(xiàn)繼承板惑。接口繼承只繼承方法簽名,而實(shí)現(xiàn)繼承則繼承實(shí)際的方法蛀缝。因?yàn)镋CMAScript中函數(shù)無法簽名刘莹,所以不支持接口繼承阎毅,只支持實(shí)現(xiàn)繼承,而實(shí)際上實(shí)現(xiàn)繼承主要依靠原型鏈實(shí)現(xiàn)点弯;
1扇调、原型鏈繼承
將原型鏈作為實(shí)現(xiàn)繼承的主要方法,其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法蒲拉。簡單回顧一下構(gòu)造函數(shù)肃拜、原型和實(shí)例的關(guān)系:每一個構(gòu)造函數(shù)都有一個原型對象,原型對象都包含一個指向構(gòu)造函數(shù)的指針雌团,而實(shí)例都包含一個指向原型對象的內(nèi)部指針燃领。
實(shí)現(xiàn)原型鏈有一種基本模式,代碼大概如下:
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();
console.log(instance.getSuperValue());
特點(diǎn):
● 非常純粹的繼承關(guān)系锦援,實(shí)例是子類的實(shí)例猛蔽,也是父類的實(shí)例
● 父類新增原型方法/原型屬性,子類都能訪問到
● 簡單,易于實(shí)現(xiàn)
缺點(diǎn):
● 要想為子類新增屬性和方法曼库,必須要在new SuperType()這樣的語句之后執(zhí)行区岗,不能放到構(gòu)造器中
● 無法實(shí)現(xiàn)多繼承
● 來自原型對象的引用屬性是所有實(shí)例共享的
● 創(chuàng)建子類實(shí)例時,無法向父類構(gòu)造函數(shù)傳參
2毁枯、借用構(gòu)造函數(shù)
在解決原型中包含引用類型值所帶來問題的過程中慈缔,開發(fā)人員開始使用一種叫做借用構(gòu)造函數(shù)的技術(shù)。這種技術(shù)的基本思想是在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)种玛。函數(shù)只是在特定環(huán)境中執(zhí)行代碼的對象藐鹤,因此通過使用apply()和call()方法也可以在新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)。
function SuperType(){
this.colors = ["red","blue","green"];
}
function SubType(){
//繼承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("yellow");
console.log(instance1.colors);
var instance2 = new SubType();
console.log(instance2.colors);
通過使用apply()和call()方法赂韵,我們實(shí)際上是在新創(chuàng)建的SubType實(shí)例的環(huán)境下調(diào)用了SuperType構(gòu)造函數(shù)娱节。這樣一來,就會在新SubType對象上執(zhí)行SuperType()函數(shù)中定義的所有對象初始化代碼祭示。這樣的話肄满,SubType的每個實(shí)例就都會具有自己的colors屬性的副本。
相對于原型鏈而言质涛,借用構(gòu)造函數(shù)有一個很大的優(yōu)勢稠歉,即可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。如下所示:
function SuperType(name){
this.name = name;
}
function SubType(){
//繼承了SuperType
SuperType.call(this,"Wendy");
//實(shí)例屬性
this.age = 20;
}
var instance1 = new SubType();
console.log(instance1.name);//“Wendy”
console.log(instance1.age);//20
特點(diǎn):
● 解決了1中蹂窖,子類實(shí)例共享父類引用屬性的問題
● 創(chuàng)建子類實(shí)例時轧抗,可以向父類傳遞參數(shù)
● 可以實(shí)現(xiàn)多繼承(call多個父類對象)
缺點(diǎn):
● 實(shí)例并不是父類的實(shí)例,只是子類的實(shí)例
● 只能繼承父類的實(shí)例屬性和方法瞬测,不能繼承原型屬性/方法
● 無法實(shí)現(xiàn)函數(shù)復(fù)用,每個子類都有父類實(shí)例函數(shù)的副本纠炮,影響性能
3月趟、組合繼承
組合繼承指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合,從而發(fā)揮二者之長的一種繼承模式恢口。主要思想:使用原型鏈實(shí)現(xiàn)對原型屬性和方法的繼承孝宗,然后通過借用函數(shù)來實(shí)現(xiàn)對實(shí)例屬性的繼承。這樣耕肩,即通過在原型上定義方法實(shí)現(xiàn)了函數(shù)復(fù)用因妇,又能夠保證每個實(shí)例都有它自己的屬性。
function SuperType(name){
this.name = name;
this.colors = ["red","blur","green"];
}
SuperType.prototype.sayName = function(){
console.log(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(){
console.log(this.age);
};
var instance1 = new SubType("Wendy",20);
instance1.colors.push("yellow");
console.log(instance1.colors); //[ 'red', 'blur', 'green', 'yellow' ]
instance1.sayName(); //Wendy
instance1.sayAge(); //20
var instance2 = new SubType("Kitty",18);
console.log(instance2.colors); //[ 'red', 'blur', 'green' ]
instance2.sayName(); //Kitty
instance2.sayAge(); //18
特點(diǎn):
● 彌補(bǔ)了方式2的缺陷猿诸,可以繼承實(shí)例屬性/方法婚被,也可以繼承原型屬性/方法
● 既是子類的實(shí)例,也是父類的實(shí)例
● 不存在引用屬性共享問題
● 可傳參
● 函數(shù)可復(fù)用
缺點(diǎn):
● 調(diào)用了兩次父類構(gòu)造函數(shù)梳虽,生成了兩份實(shí)例(子類實(shí)例將子類原型上的那份屏蔽了)