詳解JavaScript實(shí)現(xiàn)繼承的五種經(jīng)典方式(附圖解)
前言
- 在 JavaScript 中,實(shí)現(xiàn)繼承的幾種常見方式包括:
- 原型鏈繼承: 此種方式也有兩種方式
- 1.1 共享同一個父類實(shí)例
- 1.2 繞過父類實(shí)例,共享同一個類型原型
- 構(gòu)造函數(shù)繼承(借用構(gòu)造函數(shù))
- 組合繼承
- 原型式繼承
- ES6 類繼承
原型鏈繼承
原型鏈繼承-共享同一個父類實(shí)例
這是 JavaScript 最早的繼承方式,通過將子類的原型對象指向父類的實(shí)例,實(shí)現(xiàn)子類繼承父類的屬性和方法。但它有一個缺點(diǎn),就是所有子類實(shí)例共享同一個父類實(shí)例。
function Parent() { this.property = "parentProperty"; } Parent.prototype.say = function () { console.log("Parent say"); }; function Child() { this.childProperty = "childProperty"; } // 修改 Child 的 prototype 屬性指向 Parent 實(shí)例對象,那么 Child 實(shí)例對象的 __proto__ 就會指向其構(gòu)造函數(shù) Child 的 prototype 屬性(即Parent 實(shí)例對象) // 修改了 Child.prototype 的指向后,那么原來 Child.prototype 指向的對象由于被沒有引用,就會被回收。 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 自身沒有,就會沿著 __proto__ 尋找,因此找到 Parent。這明顯是不對的 console.log(Child.prototype.constructor === Child); // 輸出:false, 這明顯也是不對的
因此我們需要再修改 Child.prototype
的指向之后(即代碼 Child.prototype = new Parent();
),需要同時修改 Child.prototype.constructor
的指向:
Child.prototype = new Parent(); +Child.prototype.constructor = Child;
- 后續(xù)代碼也是同理!
原型鏈繼承-繞過父類實(shí)例,共享同一個父類的原型
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)繼承屬性。這樣每個子類實(shí)例都擁有獨(dú)立的屬性副本,但無法繼承父類原型上的方法。
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() // 報錯:childInstance.say is not a function
下圖是代碼圖解:
組合繼承
組合繼承結(jié)合了原型鏈繼承和構(gòu)造函數(shù)繼承,通過在子類構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù),然后設(shè)置子類的原型為一個父類實(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(); // 注意:修改其原型對象之后,同時必須得修改 constructor 的指向 Child.prototype.constructor = Child; const childInstance = new Child(); console.log(childInstance.property); // 輸出 'parentProperty' childInstance.say();
原型式繼承
這種繼承方式創(chuàng)建一個臨時的構(gòu)造函數(shù),將這個構(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 的指向后,那么原來的 Fn.prototype 沒有被引用,則會被回收 * 2. 那么Fn的實(shí)例對象的 __proto__ 就指向其構(gòu)造函數(shù)的 prototype */ Fn.prototype = Parent.prototype; Child.prototype = new Fn(); // 注意:修改了原型對象之后,同時必須得修改 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)鍵字,一個類可以繼承另一個類的屬性和方法。
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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript計(jì)算值然后把值嵌入到html中的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄狫avaScript計(jì)算值然后把值嵌入到html中的實(shí)現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10javascript特效實(shí)現(xiàn)——當(dāng)前時間和倒計(jì)時效果的簡單實(shí)例
下面小編就為大家?guī)硪黄猨avascript特效實(shí)現(xiàn)——當(dāng)前時間和倒計(jì)時效果的簡單實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-07-07關(guān)于Javascript 對象(object)的prototype
Javascript中的每個對象(object)都會有 prototype,下面為大家介紹下其具體的應(yīng)用2014-05-05JS+HTML5 Canvas實(shí)現(xiàn)簡單的寫字板功能示例
這篇文章主要介紹了JS+HTML5 Canvas實(shí)現(xiàn)簡單的寫字板功能,結(jié)合實(shí)例形式分析了js結(jié)合HTML5 canvas特性的圖形繪制相關(guān)操作技巧,需要的朋友可以參考下2018-08-08用JS實(shí)現(xiàn)的一個include函數(shù)
用JS實(shí)現(xiàn)的一個include函數(shù)...2007-07-07利用Promise自定義一個GET請求的函數(shù)示例代碼
這篇文章主要給大家介紹了關(guān)于如何利用Promise自定義一個GET請求的函數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Promise具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03