JavaScript繼承的實(shí)現(xiàn)方式詳解
引言
JavaScript 是一門基于原型的語言,它的繼承機(jī)制與傳統(tǒng)的基于類的面向?qū)ο缶幊蹋ㄈ?Java、C++)有所不同。盡管 ES6 引入了 class 語法,但本質(zhì)上仍然是基于原型鏈的繼承。本文將詳細(xì)介紹 JavaScript 繼承的幾種實(shí)現(xiàn)方式,包括原型鏈繼承、構(gòu)造函數(shù)繼承、組合繼承、寄生組合繼承,以及 ES6 class 繼承,并分析它們的優(yōu)缺點(diǎn)和適用場景。
一、JavaScript 繼承的基本概念
在 JavaScript 中,每個對象都有一個原型(prototype),新創(chuàng)建的對象可以從原型對象繼承屬性和方法。繼承的核心思想是:子類對象可以訪問父類的屬性和方法,從而實(shí)現(xiàn)代碼復(fù)用。
JavaScript 主要提供了以下幾種繼承方式:
- 原型鏈繼承
- 構(gòu)造函數(shù)繼承
- 組合繼承(構(gòu)造函數(shù) + 原型鏈)
- 寄生組合繼承(最優(yōu)方案)
- ES6
class
繼承
接下來,我們將逐一介紹這些繼承方式的實(shí)現(xiàn)原理、代碼示例以及優(yōu)缺點(diǎn)。
二、原型鏈繼承
1. 實(shí)現(xiàn)方式
原型鏈繼承的核心思想是讓子類的 prototype
指向父類的實(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. 存在的問題
- 子類實(shí)例共享父類的引用屬性:修改
child1.colors
之后,child2.colors
也會受到影響,因?yàn)?nbsp;colors
是父類實(shí)例的屬性,所有子類實(shí)例都共享同一個對象。 - 無法向父類構(gòu)造函數(shù)傳參:創(chuàng)建子類實(shí)例時,無法向
Parent
傳遞參數(shù)。
三、構(gòu)造函數(shù)繼承
1. 實(shí)現(xiàn)方式
通過在子類的構(gòu)造函數(shù)中調(diào)用父類構(gòu)造函數(shù),并使用 call()
或 apply()
綁定 this
,從而避免原型鏈繼承的問題。
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"] (不會被修改)
2. 解決的問題
- ? 避免了引用屬性共享問題(子類實(shí)例有自己的
colors
) - ? 支持向父類構(gòu)造函數(shù)傳參
3. 存在的問題
- 方法不能復(fù)用:父類的方法必須在每個子類實(shí)例上都創(chuàng)建一次,而不是共享在
prototype
上。
四、組合繼承(構(gòu)造函數(shù) + 原型鏈)
1. 實(shí)現(xiàn)方式
組合繼承結(jié)合了構(gòu)造函數(shù)繼承和原型鏈繼承的優(yōu)點(diǎn)。
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"]
2. 解決的問題
- ? 避免了引用屬性共享問題
- ? 支持傳參
- ? 方法可以復(fù)用
3. 存在的問題
- 方法不能復(fù)用:父類的方法必須在每個子類實(shí)例上都創(chuàng)建一次,而不是共享在
prototype
上。
四、組合繼承(構(gòu)造函數(shù) + 原型鏈)
1. 實(shí)現(xiàn)方式
組合繼承結(jié)合了構(gòu)造函數(shù)繼承和原型鏈繼承的優(yōu)點(diǎn)。
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"]
2. 解決的問題
- ? 避免了引用屬性共享問題
- ? 支持傳參
- ? 方法可以復(fù)用
3. 存在的問題
- 父類構(gòu)造函數(shù)被調(diào)用了兩次(一次在
Parent.call(this, name)
,一次在new Parent()
)。
五、寄生組合繼承(推薦方案)
1. 實(shí)現(xiàn)方式
寄生組合繼承優(yōu)化了組合繼承中的 new Parent()
,避免了父類構(gòu)造函數(shù)的重復(fù)調(diào)用。
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; } Child.prototype = Object.create(Parent.prototype); // 繼承方法,但不會執(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"]
2. 優(yōu)點(diǎn)
- ? 避免引用屬性共享問題
- ? 支持傳參
- ? 方法可以復(fù)用
- ? 避免了
new Parent()
造成的二次調(diào)用
六、ES6 class 繼承(最現(xiàn)代化的方式)
1. 實(shí)現(xiàn)方式
ES6 引入 class
語法,使繼承更加直觀:
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); // 繼承父類屬性 this.age = age; } } const child1 = new Child("Child1", 18); console.log(child1.getName()); // Child1 console.log(child1.colors); // ["red", "blue", "green"]
2. 優(yōu)點(diǎn)
- ? 語法簡潔,易讀易寫
- ? 繼承邏輯清晰
- ?
super()
關(guān)鍵字提供更清晰的繼承機(jī)制
七、總結(jié)
繼承方式 | 主要特點(diǎn) | 優(yōu)點(diǎn) | 缺點(diǎn) |
---|---|---|---|
原型鏈繼承 | 讓子類 prototype 指向父類實(shí)例 | 方法共享 | 共享引用屬性 |
構(gòu)造函數(shù)繼承 | 使用 call 調(diào)用父類 | 解決共享問題,可傳參 | 方法無法復(fù)用 |
組合繼承 | 構(gòu)造函數(shù) + 原型鏈 | 解決共享問題 | 調(diào)用兩次父類構(gòu)造函數(shù) |
寄生組合繼承 | 最優(yōu) ES5 方案 | 解決所有問題 | 代碼略復(fù)雜 |
ES6 class 繼承 | 最現(xiàn)代化方案 | 語法簡潔,推薦使用 | 僅限 ES6+ |
在現(xiàn)代 JavaScript 開發(fā)中,建議 優(yōu)先使用 ES6 class
繼承,但在需要兼容舊瀏覽器時,可以使用 寄生組合繼承。
以上就是JavaScript繼承的實(shí)現(xiàn)方式詳解的詳細(xì)內(nèi)容,更多關(guān)于JavaScript繼承方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript連接mysql與php通過odbc連接任意數(shù)據(jù)庫的實(shí)例
下面小編就為大家分享一篇javascript連接mysql與php通過odbc連接任意數(shù)據(jù)庫的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12跟我學(xué)習(xí)javascript解決異步編程異常方案
跟我學(xué)習(xí)javascript解決異步編程異常方案,感興趣的小伙伴們可以參考一下2015-11-11JavaScript交換變量的常用方法小結(jié)【4種方法】
這篇文章主要介紹了JavaScript交換變量的常用方法,結(jié)合實(shí)例形式總結(jié)分析了JavaScript交換變量的4種實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2020-05-05zTree獲取當(dāng)前節(jié)點(diǎn)的下一級子節(jié)點(diǎn)數(shù)實(shí)例
下面小編就為大家?guī)硪黄獄Tree獲取當(dāng)前節(jié)點(diǎn)的下一級子節(jié)點(diǎn)數(shù)實(shí)例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09在JavaScript中調(diào)用OpenAI?API的詳細(xì)步驟
在?JavaScript?中調(diào)用?OpenAI?API?也非常簡單,下面我將結(jié)合具體代碼示例以及使用場景,詳細(xì)講解如何使用?JavaScript?調(diào)用?OpenAI?API,需要的朋友可以參考下2025-04-04javascript Onunload與Onbeforeunload使用小結(jié)
Onunload,onbeforeunload都是在刷新或關(guān)閉時調(diào)用,可以在<script>腳本中通過window.onunload來指定或者在<body>里指定。區(qū)別在于onbeforeunload在onunload之前執(zhí)行,它還可以阻止onunload的執(zhí)行。2009-12-12JS實(shí)現(xiàn)的簡單輪播圖運(yùn)動效果示例
這篇文章主要介紹了JS實(shí)現(xiàn)的簡單輪播圖運(yùn)動效果,結(jié)合完整實(shí)例形式分析了javascript基于定時器動態(tài)修改頁面元素屬性的相關(guān)操作技巧,需要的朋友可以參考下2016-12-12Javascript 阻止javascript事件冒泡,獲取控件ID值
Javascript學(xué)習(xí)日記-阻止javascript事件冒泡,獲取控件ID值2009-06-06