詳解JavaScript實(shí)現(xiàn)繼承的五種經(jīng)典方式(附圖解)
前言
- 在 JavaScript 中,實(shí)現(xiàn)繼承的幾種常見方式包括:
- 原型鏈繼承: 此種方式也有兩種方式
- 1.1 共享同一個(gè)父類實(shí)例
- 1.2 繞過父類實(shí)例,共享同一個(gè)類型原型
- 構(gòu)造函數(shù)繼承(借用構(gòu)造函數(shù))
- 組合繼承
- 原型式繼承
- ES6 類繼承
原型鏈繼承
原型鏈繼承-共享同一個(gè)父類實(shí)例
這是 JavaScript 最早的繼承方式,通過將子類的原型對(duì)象指向父類的實(shí)例,實(shí)現(xiàn)子類繼承父類的屬性和方法。但它有一個(gè)缺點(diǎn),就是所有子類實(shí)例共享同一個(gè)父類實(shí)例。
function Parent() {
this.property = "parentProperty";
}
Parent.prototype.say = function () {
console.log("Parent say");
};
function Child() {
this.childProperty = "childProperty";
}
// 修改 Child 的 prototype 屬性指向 Parent 實(shí)例對(duì)象,那么 Child 實(shí)例對(duì)象的 __proto__ 就會(huì)指向其構(gòu)造函數(shù) Child 的 prototype 屬性(即Parent 實(shí)例對(duì)象)
// 修改了 Child.prototype 的指向后,那么原來(lái) Child.prototype 指向的對(duì)象由于被沒有引用,就會(huì)被回收。
Child.prototype = new Parent();
const childInstance = new Child();
console.log(childInstance.property); // 輸出 'parentProperty'
childInstance.say();下圖是代碼圖解:

上面的代碼看似沒有問題,但其實(shí)還是存在缺陷:
console.log(Child.prototype.constructor); // 輸出:[Function: Parent], 即是 Parent 構(gòu)造函數(shù),這是由于 Child.prototype 自身沒有,就會(huì)沿著 __proto__ 尋找,因此找到 Parent。這明顯是不對(duì)的 console.log(Child.prototype.constructor === Child); // 輸出:false, 這明顯也是不對(duì)的
因此我們需要再修改 Child.prototype 的指向之后(即代碼 Child.prototype = new Parent(); ),需要同時(shí)修改 Child.prototype.constructor 的指向:
Child.prototype = new Parent(); +Child.prototype.constructor = Child;
- 后續(xù)代碼也是同理!
原型鏈繼承-繞過父類實(shí)例,共享同一個(gè)父類的原型
function Parent() {
this.property = "parentProperty";
}
Parent.prototype.say = function () {
console.log("Parent say");
};
function Child() {
Parent.call(this);
this.childProperty = "childProperty";
}
// 方式一:直接指向
// Child.prototype.__proto__ = Parent.prototype;
// 方式二:使用 Object.create(),這是 es5 的方法
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
let childInstance = new Child();
console.log(childInstance.property);
childInstance.say();下圖是代碼圖解:

構(gòu)造函數(shù)繼承(借用構(gòu)造函數(shù))
這種方式通過在子類構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù),實(shí)現(xiàn)繼承屬性。這樣每個(gè)子類實(shí)例都擁有獨(dú)立的屬性副本,但無(wú)法繼承父類原型上的方法。
function Parent() {
this.property = "parentProperty";
}
Parent.prototype.say = function () {
console.log("Parent say");
};
function Child() {
Parent.call(this);
this.childProperty = "childProperty";
}
const childInstance = new Child();
console.log(childInstance.property); // 輸出 'parentProperty'
// childInstance.say() // 報(bào)錯(cuò):childInstance.say is not a function下圖是代碼圖解:

組合繼承
組合繼承結(jié)合了原型鏈繼承和構(gòu)造函數(shù)繼承,通過在子類構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù),然后設(shè)置子類的原型為一個(gè)父類實(shí)例,實(shí)現(xiàn)了既能繼承屬性又能繼承方法。
function Parent() {
this.property = "parentProperty";
}
Parent.prototype.say = function () {
console.log("Parent say");
};
function Child() {
Parent.call(this);
this.childProperty = "childProperty";
}
Child.prototype = new Parent();
// 注意:修改其原型對(duì)象之后,同時(shí)必須得修改 constructor 的指向
Child.prototype.constructor = Child;
const childInstance = new Child();
console.log(childInstance.property); // 輸出 'parentProperty'
childInstance.say();
原型式繼承
這種繼承方式創(chuàng)建一個(gè)臨時(shí)的構(gòu)造函數(shù),將這個(gè)構(gòu)造函數(shù)的原型指向父構(gòu)造函數(shù)的原型,再將子構(gòu)造函數(shù)的原型指向該構(gòu)造函數(shù)的實(shí)例,從而實(shí)現(xiàn)繼承。
function mockExtend(Parent, Child) {
function Fn() {}
/**
* 1. 修改了 Fn.prototype 的指向后,那么原來(lái)的 Fn.prototype 沒有被引用,則會(huì)被回收
* 2. 那么Fn的實(shí)例對(duì)象的 __proto__ 就指向其構(gòu)造函數(shù)的 prototype
*/
Fn.prototype = Parent.prototype;
Child.prototype = new Fn();
// 注意:修改了原型對(duì)象之后,同時(shí)必須得修改 constructor 的指向
Child.prototype.constructor = Child;
}
// =============================== 使用 ==================================
function Parent() {
this.property = "parentProperty";
}
Parent.prototype.say = function () {
console.log("Parent say");
};
function Child() {
Parent.call(this);
this.childProperty = "childProperty";
}
mockExtend(Parent, Child);
const childInstance = new Child();
console.log(childInstance.property);
childInstance.say();下圖是代碼圖解:

ES6 類繼承
ES6 引入了 class 關(guān)鍵字,使繼承更加易讀和易用。通過 extends 關(guān)鍵字,一個(gè)類可以繼承另一個(gè)類的屬性和方法。
class Parent {
constructor() {
this.property = "parentProperty";
}
say() {
console.log("Parent say");
}
}
class Child extends Parent {
constructor() {
super();
this.childProperty = "childProperty";
}
}
const childInstance = new Child();
console.log(childInstance.property); // 輸出 'parentProperty'
childInstance.say();到此這篇關(guān)于詳解JavaScript實(shí)現(xiàn)繼承的五種經(jīng)典方式(附圖解)的文章就介紹到這了,更多相關(guān)JavaScript實(shí)現(xiàn)繼承方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Ajax 文件上傳進(jìn)度監(jiān)聽之upload.onprogress案例詳解
open 動(dòng)態(tài)修改img的onclick事件示例代碼
javascript實(shí)現(xiàn)百度地圖鼠標(biāo)滑動(dòng)事件顯示、隱藏
JS實(shí)現(xiàn)簡(jiǎn)單易用的手機(jī)端浮動(dòng)窗口顯示效果
JavaScript實(shí)現(xiàn)LI列表數(shù)據(jù)綁定的方法
JavaScript 實(shí)現(xiàn)模態(tài)對(duì)話框 源代碼大全

