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