JavaScript實(shí)現(xiàn)類繼承的方法最全講解
先貼一張圖:

1、原型鏈繼承
原型鏈繼承的核心思想是讓子類的 prototype 指向父類的實(shí)例,這樣子類就能訪問父類的方法和屬性。
缺點(diǎn):
- 子類實(shí)例共享父類的引用屬性:修改
child1.colors之后,child2.colors也會(huì)受到影響,因?yàn)?colors是父類實(shí)例的屬性,所有子類實(shí)例都共享同一個(gè)對(duì)象。 - 無法向父類構(gòu)造函數(shù)傳參:創(chuàng)建子類實(shí)例時(shí),無法向
Parent傳遞參數(shù)。
例子:
function Parent() {
this.name = "Parent";
this.colors = ["red", "blue", "green"];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child() {}
Child.prototype = new Parent(); // 關(guān)鍵點(diǎn):讓子類的 prototype 指向父類實(shí)例
const child1 = new Child();
console.log(child1.getName()); // Parent
console.log(child1.colors); // ["red", "blue", "green"]
child1.colors.push("yellow");
const child2 = new Child();
console.log(child2.colors); // ["red", "blue", "green", "yellow"] (被修改了)
2、構(gòu)造函數(shù)
通過在子類的構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù),并使用 call() 或 apply() 綁定 this,從而避免原型鏈繼承的問題。
缺點(diǎn):
- 子類無法繼承父類在原型鏈上的屬性和方法。
- 每個(gè)實(shí)例都拷貝一份,占用內(nèi)存大,尤其是方法過多的時(shí)候。(函數(shù)復(fù)用又無從談起了,本來我們用 prototype 就是解決復(fù)用問題的)
優(yōu)點(diǎn):解決了通過原型鏈繼承子類對(duì)于父類引用類型屬性的修改,導(dǎo)致其他子類實(shí)列共享了修改的問題
例子:
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
function Child(name) {
Parent.call(this, name); // 關(guān)鍵點(diǎn):在子類構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù)
}
const child1 = new Child("Child1");
const child2 = new Child("Child2");
child1.colors.push("yellow");
console.log(child1.colors); // ["red", "blue", "green", "yellow"]
console.log(child2.colors); // ["red", "blue", "green"] (不會(huì)被修改)
3、組合繼承
組合繼承(原型鏈繼承+借用構(gòu)造函數(shù)繼承)由于這兩種繼承方式都存在各自的優(yōu)缺點(diǎn),從而將他們優(yōu)點(diǎn)結(jié)合起來,通過原型繼承父類原型上的屬性和方法,通過構(gòu)造函數(shù)的方法繼承父類的屬性
缺點(diǎn):組合繼承是js最常用的繼承模式,組合繼承最大的問題就是無論在什么情況下,都會(huì)調(diào)用兩次構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型時(shí),另一次是在子類構(gòu)造函數(shù)內(nèi)部。
優(yōu)點(diǎn):就是可以把方法定義在原型上復(fù)用,每個(gè)實(shí)例又有自己的屬性。組合繼承彌補(bǔ)了原型鏈和盜用構(gòu)造函數(shù)的不足,是js中使用最多的繼承模式。
例子:
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name); // 調(diào)用父類構(gòu)造函數(shù),繼承實(shí)例屬性
this.age = age;
}
Child.prototype = new Parent(); // 繼承父類方法
Child.prototype.constructor = Child; // 修正 constructor 指向
const child1 = new Child("Child1", 18);
console.log(child1.getName()); // Child1
console.log(child1.colors); // ["red", "blue", "green"]解決的問題:
- 避免了引用屬性共享問題
- 支持傳參
- 方法可以復(fù)用
存在的問題:
- 父類構(gòu)造函數(shù)被調(diào)用了兩次
4、寄生組合繼承
組合繼承存在這一定的效率問題,它的父類構(gòu)造函數(shù)始終會(huì)被調(diào)用倆次,一次在創(chuàng)建字類原型時(shí)調(diào)用,另一次在子類構(gòu)造函數(shù)中調(diào)用。本質(zhì)上子類只需要在執(zhí)行時(shí)重寫自己的原型就行了。寄生式組合繼承可以算是引用類型繼承的最佳模式。
寄生組合繼承優(yōu)化了組合繼承中的 new Parent(),避免了父類構(gòu)造函數(shù)的重復(fù)調(diào)用。
優(yōu)點(diǎn):
- 避免引用屬性共享問題
- 支持傳參
- 方法可以復(fù)用
- 避免了 new Parent() 造成的二次調(diào)用
其中拓展一下,Object.create(Parent.prototype)的原理
Object.create()會(huì)創(chuàng)建一個(gè)新對(duì)象,并將其__proto__指向參數(shù)對(duì)象:
Child的實(shí)例能通過原型鏈訪問Parent.prototype的方法(如getName)。但不會(huì)在
Child.prototype上生成多余的父類屬性(如colors)。Child.prototype = Object.create(Parent.prototype); // 相當(dāng)于: Child.prototype = {}; Child.prototype.__proto__ = Parent.prototype;
例子:
function Parent(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name, age) {
Parent.call(this, name); // 繼承屬性
this.age = age;
}
// 實(shí)現(xiàn)原型方法繼承。只繼承原型方法,不調(diào)用構(gòu)造函數(shù)
Child.prototype = Object.create(Parent.prototype); // 繼承方法,但不會(huì)執(zhí)行 Parent 構(gòu)造函數(shù)
Child.prototype.constructor = Child; // 修正 constructor 指向
const child1 = new Child("Child1", 18);
console.log(child1.getName()); // Child1
console.log(child1.colors); // ["red", "blue", "green"]
5、ES6中class+extends 的繼承
ES6中引入了class關(guān)鍵字,class可以通過extends關(guān)鍵字實(shí)現(xiàn)繼承,還可以通過static關(guān)鍵字定義類的靜態(tài)方法,這比 ES5 的通過修改原型鏈實(shí)現(xiàn)繼承,要清晰和方便很多。
優(yōu)點(diǎn):
- 語法簡潔,易讀易寫
- 繼承邏輯清晰
- super()??????? 關(guān)鍵字提供更清晰的繼承機(jī)制
ES5 的繼承,實(shí)質(zhì)是先創(chuàng)造子類的實(shí)例對(duì)象this,然后再將父類的方法添加到this上面(Parent.apply(this))。ES6 的繼承機(jī)制完全不同,實(shí)質(zhì)是先將父類實(shí)例對(duì)象的屬性和方法,加到this上面(所以必須先調(diào)用super方法),然后再用子類的構(gòu)造函數(shù)修改this。
關(guān)鍵點(diǎn):
super(name)等價(jià)于Parent.call(this, name)(繼承實(shí)例屬性)。extends通過Object.create()實(shí)現(xiàn)原型方法的繼承。
例子:
class Parent {
constructor(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
getName() {
return this.name;
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 調(diào)用父類構(gòu)造函數(shù),初始化子類的實(shí)例屬性(相當(dāng)于 Parent.call(this))
this.age = age;
}
}
const child1 = new Child("Child1", 18);
console.log(child1.getName()); // Child1
console.log(child1.colors); // ["red", "blue", "green"]
6、原型式繼承(額外拓展一個(gè))
原型式繼承 是 原型鏈繼承 和 構(gòu)造函數(shù)繼承 的折中方案,直接基于對(duì)象創(chuàng)建新對(duì)象。
- 優(yōu)點(diǎn):不需要定義子類構(gòu)造函數(shù),已經(jīng)原型鏈.prototype的指向操作。
- 缺點(diǎn):父類構(gòu)造函數(shù)的變量值還是會(huì)共享,保持原型鏈繼承的特征。
例子:
// Object.create()底層實(shí)現(xiàn)原理 (ES6的新特性)
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
let person = {
name:'薩哈',
things:[]
}
let a = Object.create(person)
a.name = '小紅'
a.things.push('筆')
console.log('a',a)
let b = Object.create(person,{
//定義該原型對(duì)象里每個(gè)變量的屬性
name:{
//該變量的值
value:'小黃',
//該變量是否可寫
writable: false,
//該變量是否能遍歷
enumerable:true,
//是否允許外部操作修改這個(gè)變量的其它屬性值(value屬性除外)
configurable:true
}
})
b.name = '我要修改'
console.log('b',b)總結(jié)
到此這篇關(guān)于JavaScript實(shí)現(xiàn)類繼承方法的文章就介紹到這了,更多相關(guān)JS實(shí)現(xiàn)類繼承內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中fromCharCode 和 fromCodePoint 的詳解與應(yīng)
本文將詳細(xì)介紹 JavaScript 中的 String.fromCharCode 和 String.fromCodePoint 方法,這兩個(gè)方法能夠幫助開發(fā)者高效地處理字符與編碼之間的轉(zhuǎn)換,理解它們的區(qū)別與應(yīng)用,將讓你的代碼更加靈活和高效,感興趣的朋友跟隨小編一起看看吧2024-12-12
JavaScript實(shí)現(xiàn)滾動(dòng)加載更多
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)滾動(dòng)加載更多,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
bootstrapValidator自定驗(yàn)證方法寫法
這篇文章主要為大家詳細(xì)介紹了bootstrapValidator自定驗(yàn)證方法寫法,研究bootstrapValidator驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
js實(shí)現(xiàn)圖片切換(動(dòng)畫版)
本文主要對(duì)javascript實(shí)現(xiàn)圖片切換(動(dòng)畫版)的方法進(jìn)行步驟分析、實(shí)例介紹,具有很好的參考價(jià)值,需要的朋友一起來看下吧2016-12-12
基于JavaScript實(shí)現(xiàn)動(dòng)態(tài)雨滴特效
這篇文章主要為大家詳細(xì)介紹了如何利用JavaScript實(shí)現(xiàn)動(dòng)態(tài)雨滴特效并且點(diǎn)擊雨滴會(huì)有雨滴爆裂的效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
讓FireFox支持innerText的實(shí)現(xiàn)代碼
DHTML非標(biāo)準(zhǔn)屬性innerText在FireFox中的使用2009-12-12

