原型鏈繼承
// 父類
function Parent(name) {
this.name = name;
}
Parent.prototype.age = 20; // 在父類原型上添加屬性
// 子類
function Per() {
this.name = 'Rory';
}
Per.prototype = new Parent();
const per = new Per();
console.log(per)
打印結(jié)果如下:
上面定義了一個 Parent
類并傳了一個 name
值解幼,而且在 Parent
的原型上添加了一個 age
屬性放仗,然后定義了一個子類 Per
润绎,并將子類的原型設(shè)置為 Parent
的實例,這種方式是利用原型來實現(xiàn)繼承匙监,它就是讓新實例的原型等于父類的實例凡橱。這種繼承方式可繼承的屬性有:
- 實例的構(gòu)造函數(shù)的屬性
- 父類構(gòu)造函數(shù)的屬性,比如
Parent
的name
- 父類原型的屬性 比如
age
缺點:
- 無法向父類構(gòu)造函數(shù)傳參亭姥,比如上圖中新實例的原型中的
name
為undefined
- 所有新實例都共用父類的屬性,如果一個實例修改了原型會影響其它的實例
構(gòu)造函數(shù)繼承
// 父類
function Parent(name) {
this.name = name;
}
Parent.prototype.age = 20; // 在父類原型上添加屬性
function Per() {
Parent.call(this, 'Rory');
this.sex = '男';
}
const per = new Per();
console.log(per)
打印結(jié)果如下
構(gòu)造函數(shù)繼承使用了 call()
或 apply()
將父類構(gòu)造函數(shù)引入子類函數(shù)顾稀,這種繼承方式的特點有:
- 只繼承父類構(gòu)造函數(shù)的屬性达罗,沒有繼承父類原型上的屬性
- 解決了原型鏈繼承的缺點
- 可以繼承多個構(gòu)造函數(shù)
- 在子類的實例中可以傳入父類的實例參數(shù)
缺點:
- 只能繼承父類構(gòu)造函數(shù)的屬性
- 每次使用都得重新調(diào)用,無法復(fù)用
- 每個新實例都有父類構(gòu)造函數(shù)的屬性静秆,會比較臃腫
組合式繼承
// 父類
function Parent(name) {
this.name = name;
}
Parent.prototype.age = 20; // 在父類原型上添加屬性
function Per(name) {
Parent.call(this, name);
}
Per.prototype = new Parent();
const per = new Per('Rory');
console.log(per);
打印結(jié)果如下:
這種繼承方式通過調(diào)用父類構(gòu)造粮揉,繼承父類的屬性并保留傳參的優(yōu)點,然后通過將父類實例作為子類原型抚笔,實現(xiàn)函數(shù)復(fù)用
缺點:
- 調(diào)用了兩次父類構(gòu)造函數(shù)扶认,所以會比較耗內(nèi)存
- 子類的構(gòu)造函數(shù)會代替原型上的那個父類構(gòu)造函數(shù)。
原型式繼承
// 父類
function Parent(name) {
this.name = name;
}
Parent.prototype.age = 20; // 在父類原型上添加屬性
// 封裝一個函數(shù)
function content(obj) {
function F(){};
F.prototype = obj;
return new F();
}
const p = new Parent('Rory');
const per = content(p);
console.log(per);
console.log(per.name);
console.log(per.age);
打印結(jié)果如下:
這種繼承方式是先定義一個函數(shù) content
殊橙,然后在內(nèi)部定義一個構(gòu)造函數(shù) F
辐宾,將該構(gòu)造函數(shù)的原型設(shè)置為傳入的參數(shù),參數(shù)是一個對象膨蛮,然后將 F
實例化后返回叠纹。
缺點:
- 所有實例都會繼承原型上的屬性
- 無法復(fù)用,返回的是一個實例敞葛,屬性要另外添加
寄生式繼承
// 父類
function Parent(name) {
this.name = name;
}
Parent.prototype.age = 20; // 在父類原型上添加屬性
// 封裝一個函數(shù)
function content(obj) {
function F(){};
F.prototype = obj;
return new F();
}
const Per = new Parent();
function subObject(obj) {
const per = content(obj);
per.name = 'Rory';
return per;
}
const p = subObject(Per);
console.log(p);
打印結(jié)果如下:
這種繼承方式相當(dāng)于給原型式繼承外面套了個殼子誉察,可以在 subObject
里新增屬性或方法
優(yōu)點:
- 沒有創(chuàng)建自定義類型,因為只是套了個殼子返回對象惹谐,這個函數(shù)就成了創(chuàng)建的新對象
缺點:
- 沒用到原型持偏,無法復(fù)用
寄生組合式繼承
// 父類
function Parent(name) {
this.name = name;
}
Parent.prototype.age = 20; // 在父類原型上添加屬性
function content(obj) {
function F(){}
F.prototype = obj;
return new F();
}
const Per = content(Parent.prototype);
function subObject() {
Parent.call(this, 'Rory');
}
subObject.prototype = Per;
Per.constructor = subObject; // 修復(fù)實例
const p = new subObject();
console.log(p)
打印結(jié)果如下:
這種繼承方式修復(fù)了組合繼承的問題驼卖,在函數(shù)內(nèi)返回對象然后調(diào)用,函數(shù)的原型等于另一個實例鸿秆。在函數(shù)中用 apply
或者 call
引入另一個構(gòu)造函數(shù)款慨,可傳參
特點:
- 可以向父類傳參
- 子類實例可以繼承到父類原型上的屬性
- 可以復(fù)用
ES6中的繼承
通過 class
定義一個類,再用 extends
進(jìn)行繼承谬莹,通過 super
繼承父類的屬性或方法
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
}
drink() {
console.log('喝水')
}
}
class Per extends Parent {
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
drink() {
super.drink();
}
}
const p = new Per('Rory', 22, '男');
console.log(p)
打印結(jié)果如下: