JS實現繼承主要有以下幾種方式,我將分別舉例說明:
1. 原型鏈繼承:
這是最基本的繼承方式,核心是將子型別的原型指向父型別的例項。
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log("My name is " + this.name);
};
function Child(name, age) {
this.age = age;
}
Child.prototype = new Parent("Parent Name"); // Child的原型指向Parent的例項
Child.prototype.constructor = Child; // 修正constructor指向
let child1 = new Child("Child Name", 10);
child1.sayName(); // 輸出: My name is Parent Name (繼承自Parent)
console.log(child1.age); // 輸出: 10
console.log(child1 instanceof Parent); // 輸出: true
console.log(child1 instanceof Child); // 輸出: true
優點: 簡單易懂。
缺點:
- 所有子類例項共享父類例項的屬性,修改一個例項的屬性會影響其他例項。
- 建立子類例項時無法向父類建構函式傳參。
2. 建構函式繼承 (借用建構函式):
透過在子型別建構函式中呼叫父型別建構函式來繼承父型別的屬性。
function Parent(name) {
this.name = name;
}
function Child(name, age) {
Parent.call(this, name); // 呼叫Parent建構函式
this.age = age;
}
let child1 = new Child("Child Name", 10);
child1.sayName(); // 報錯: child1.sayName is not a function (沒有繼承父類方法)
console.log(child1.name); // 輸出: Child Name
console.log(child1.age); // 輸出: 10
console.log(child1 instanceof Parent); // 輸出: false
console.log(child1 instanceof Child); // 輸出: true
優點:
- 解決了原型鏈繼承共享屬性的問題。
- 可以在建立子類例項時向父類建構函式傳參。
缺點:
- 無法繼承父類原型上的方法和屬性,只能繼承父類建構函式中的屬性。
- 每次建立子類例項都會重複執行父類建構函式中的程式碼,造成效能浪費。
3. 組合繼承 (原型鏈繼承 + 建構函式繼承):
結合了原型鏈繼承和建構函式繼承的優點。
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function() {
console.log("My name is " + this.name);
};
function Child(name, age) {
Parent.call(this, name); // 繼承屬性
this.age = age;
}
Child.prototype = Object.create(Parent.prototype); // 繼承方法
Child.prototype.constructor = Child; // 修正constructor指向
let child1 = new Child("Child Name", 10);
child1.sayName(); // 輸出: My name is Child Name
console.log(child1.age); // 輸出: 10
console.log(child1 instanceof Parent); // 輸出: true
console.log(child1 instanceof Child); // 輸出: true
優點: 結合了兩種繼承方式的優點,既可以繼承父類建構函式中的屬性,也可以繼承父類原型上的方法。
缺點: 父類建構函式會被呼叫兩次(一次在建立子類原型時,一次在子類建構函式中),造成一定的效能浪費。
4. ES6 Class 繼承 (extends):
ES6 提供了更簡潔的 class
語法和 extends
關鍵字來實現繼承。
class Parent {
constructor(name) {
this.name = name;
}
sayName() {
console.log("My name is " + this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 呼叫父類建構函式
this.age = age;
}
}
let child1 = new Child("Child Name", 10);
child1.sayName(); // 輸出: My name is Child Name
console.log(child1.age); // 輸出: 10
console.log(child1 instanceof Parent); // 輸出: true
console.log(child1 instanceof Child);