這裏主要講4種ES5繼承以及ES6的class語法糖的繼承。
這裏給出父類的定義:
function SuperType(name) {
this.name = name;
}
SuperType.prototype.sayName = function () {
console.log(this.name);
};
1、原型鏈繼承
核心:將父類的實例作爲子類的原型
function SubType(name) {
this.name = name;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
此時原型對象的constructor的執行變成了SuperType,因爲constructor指向構造函數,所以重新指向子類的構造函數即可,即Subtype.prototype.constructor = SubType;
優點
1)非常純粹的繼承關係,實例是子類的實例,也是父類的實例,通過instanceof可以判斷
2)父類新增原型方法/屬性,子類都可以訪問到
3)簡單、易於實現
缺點
1)要想子類新增屬性和方法,必須要在new SuperType()這樣的語句之後執行,不能放到構造函數中
2)無法實現多繼承
3)來自原型對象的引用屬性是所有實例共享的
4)創建子類實例時,無法向父類構造函數傳參
2、構造函數繼承
核心:使用父類的構造函數來增強子類實例,等於是複製父類的實例屬性給子類,在子類的構造函數內使用SuperType.call(this);
function SubType(name) {
SuperType.call(this, name);
}
優點
1、解決了原型鏈繼承中,子類實例共享父類屬性的問題
2、創建子類實例是,可以向父類傳遞參數
3、可以實現多繼承(call多個父類對象)
缺點
1、實例並不是父類的實例,只是子類的實例(instanceof可以判斷)
2、只能繼承父類的實例屬性和方法,不能繼承原型屬性/方法
3、無法實現函數服用,每個子類都有父類的實例函數的副本,影響性能
3、組合繼承(構造函數繼承和原型鏈繼承的結合)
核心:在構造函數內利用call方法,直接調用屬性,然後在原型prototype上綁定綁定方法(通過實例化new父對象),然後修改constructor指向子對象的構造函數。
function SubType(name){
SuperType.call(this,name);//第二次調用父類
}
SubType.prototype = new SuperType();//第一調用父類
SubType.prototype.constructor = SubType;
優點
1、既是子類的實例,也是父類的實例
2、不存在引用屬性共享問題
缺點
1、調用了兩次父類構造函數,生成了兩份實例(子類實例將子類原型上的那份屏蔽了)
4、寄生組合繼承(目前最優)
核心:通過寄生方式,砍掉父類的實例屬性,這樣在調用兩次父類的構造函數的時候,就不會初始化兩次實例方法/屬性,避免了組合繼承的缺點
function SubType(name) {
SuperType.call(this,name);
}
(function () {
var Super = function () {
//創建沒有實例的方法的類
};
Super.prototype = SuperType.prototype;
SubType.prototype = new Super();//將實例作爲子類的原型
SubType.prototype.constructor = SubType;
})();
5、ES6繼承
核心:通過ES6語法糖class,使用extends關鍵字
class SubType extends SuperType{
constructor(...args){
super(...args);
}
}
通過在class的constructor方法內調用super,調用父類的constructor方法,用於新建父類的this對象。
SubType.__proto__ === SuperType;//true
SubType.prototype.__proto__ === SuperType.prototype;//true
只要帶有prototype屬性的函數就能被extends關鍵字繼承
部分內容參考《JS高級程序設計第三版》和《ES6標準入門第三版》