1. 原型鏈繼承
基本思想:利用原型讓一個引用類型繼承另一個引用類型的屬性和方法邦邦。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function () {
return this.property;
}
function SubType () {
this.subproperty = false;
}
// 原型對象設(shè)置為另一個類型的實例
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
}
var instance = new SubType();
缺點:
- 原型中包含引用類型的值,會被所有實例共享燃辖,發(fā)生改變會相互影響鬼店。
- 在創(chuàng)建子類型的實例時,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)黔龟。
注意:這里指的是無法達到傳遞參數(shù)的目的妇智,因為子類型的原型上的屬性會被所有實例共享滥玷。
2. 借用構(gòu)造函數(shù)繼承
基本思想:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。
function SuperType(name) {
this.name = name;
}
function SubType(name) {
SuperType.call(this, name);
this.age = 26;
}
var instance_1 = new SubType('lee');
var instance_2 = new SubType('tom');
console.log(instance_1.name); // lee
console.log(instance_2.name); // tom
缺點:
- 方法都在構(gòu)造函數(shù)中定義巍棱,無法復用惑畴。
- 無法繼承超類型原型上的屬性和方法。
3. 組合繼承
基本思路:使用原型鏈實現(xiàn)對原型屬性和方法的繼承拉盾,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承桨菜。
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
// 繼承超類型的原型
SubType.prototype = new SuperType();
// 修正 constructor 屬性
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function () {
console.log(this.age);
}
var instance = new SubType('lee', 26);
下圖中綠色標注是實例上的屬性,紅色標注的是從超類型原型上繼承的屬性捉偏。
缺點:
- 調(diào)用兩次超類型構(gòu)造函數(shù)倒得。
4. 原型式繼承
基本思想:借助原型基于已有的對象創(chuàng)建新對象。
本質(zhì)上講夭禽,object() 對傳入其中的對象執(zhí)行了一次淺復制霞掺。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
ECMAScript 5 通過新增 Object.create()
方法規(guī)范化了原型式繼承。
缺點:
- 原型中包含引用類型的值讹躯,會被所有實例共享菩彬,發(fā)生改變會相互影響。
- 在創(chuàng)建子類型的實例時潮梯,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)骗灶。
注意:這里指的是無法達到傳遞參數(shù)的目的,因為子類型的原型上的屬性會被所有實例共享秉馏。
5. 寄生式繼承
基本思想:創(chuàng)建一個僅用于封裝繼承過程的函數(shù)耙旦,該函數(shù)在內(nèi)部以某種方式來增強對象。
使用場景:在主要考慮對象而不是自定義類型和構(gòu)造函數(shù)的情況下萝究。
function createAnother(original) {
var clone = object(original);
// 增強
clone.sayHi = function() {
console.log('hi');
}
return clone;
}
缺點:
- 函數(shù)無法復用
- 同原型式繼承
6. 寄生組合式繼承
基本思路:通過借用構(gòu)造函數(shù)來繼承屬性免都,通過原型鏈的混成形式來繼承方法。
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype); // 創(chuàng)建超類型的副本
prototype.constructor = subType; // 修正 constructor 屬性
subType.prototype = prototype;
}
function SuperType(name) {
this.name = name;
this.color = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function () {
console.log(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function () {
console.log(this.age);
}
下圖中:
紅色標注的是:通過構(gòu)造函數(shù)繼承的超類型的實例屬性
黃色標注的是:子類型原型上的方法
綠色標注的是:超類型原型上的方法
7. 多重繼承
基本思路:通過 Object.assign()
將多個原型拷貝到一起帆竹,實現(xiàn)對原型上方法的繼承绕娘。
function AType () {
this.a = 'A';
}
AType.prototype.sayA = function () {
console.log('A');
}
function BType () {
this.b = 'B';
}
BType.prototype.sayB = function () {
console.log('B');
}
function TargetType () {
// 借用構(gòu)造函數(shù)
AType.call(this);
BType.call(this);
}
// 以 AType 的原型對象為模板創(chuàng)建一個新對象
TargetType.prototype = Object.create(AType.prototype);
// 將 BType 的原型對象淺拷貝到 TargetType 的原型對象上
Object.assign(TargetType.prototype, BType.prototype);
TargetType.prototype.constructor = TargetType;
下圖中:
紅色標注的是:通過構(gòu)造函數(shù)繼承的其它類型的實例屬性
綠色標注的是:B類型原型上的方法
黃色標注的是:A類型原型上的方法
8. ES6 extends
繼承了其它類的類被稱為派生類。
- 派生類有默認的構(gòu)造器栽连,并自動調(diào)用
super()
险领。 - 如果派生類指定了構(gòu)造器,就需要使用
super()
秒紧,否則會造成錯誤舷暮。
在構(gòu)造器中,必須在訪問 this 之前調(diào)用 super() 方法噩茄,因為 super() 負責初始化 this。
class SuperType {
constructor() {
this.superName = 'super';
}
superSay() {
console.log('super');
}
}
class SubType extends SuperType {
constructor(...args) {
super(...args);
this.subName = 'sub';
}
subSay() {
console.log('sub');
}
}
參考
JavaScript 高級程序設(shè)計
JavaScript常用八種繼承方案
阮一峰 JavaScript 教程: 對象的繼承