深入理解ES6中類(class)的底層原理

ES6 引入了類(class)這一特性,使得 JavaScript 的面向對象編程風格更加簡潔和易於理解。儘管類看起來像其他編程語言中的經典面向對象模型,但其底層依然是 JavaScript 早已存在的原型繼承機制。本文將深入解析 ES6 類的底層原理,幫助你更好地理解其工作機制。

1. 類本質是構造函數

在 ES6 中定義的類,實際上是一個構造函數。你可以通過 typeof 關鍵字驗證類的本質:

1
class Person {
2
constructor(name) {
3
this.name = name;
4
}
5
}
6
7
console.log(typeof Person); // "function"

類的定義最終會被編譯成一個函數,這意味著類只是構造函數的一種語法糖。

2. 構造函數的工作原理

類中的 constructor 方法是用於初始化類實例的構造函數。在創建類的實例時,new 關鍵字會調用這個構造函數:

1
class Person {
2
constructor(name) {
3
this.name = name;
4
}
5
}
6
7
const p = new Person("Alice");
8
console.log(p.name); // "Alice"

這與傳統的 ES5 函數構造器極為相似:

1
function Person(name) {
2
this.name = name;
3
}
4
5
const p = new Person("Alice");
6
console.log(p.name); // "Alice"

3. 原型繼承

類中的方法是在其原型上定義的,這與 ES5 的原型繼承機制完全一致。每當定義一個類的方法時,實際上是將這些方法添加到該類的原型上:

1
class Person {
2
constructor(name) {
3
this.name = name;
4
}
5
6
greet() {
7
console.log(`Hello, ${this.name}!`);
8
}
9
}
10
11
const p = new Person("Alice");
12
p.greet(); // "Hello, Alice!"

在 ES5 中實現相同效果的方法如下:

1
function Person(name) {
2
this.name = name;
3
}
4
5
Person.prototype.greet = function () {
6
console.log(`Hello, ${this.name}!`);
7
};
8
9
const p = new Person("Alice");
10
p.greet(); // "Hello, Alice!"

類的底層繼承機制仍然是通過原型鏈實現的。

4. 類繼承機制

ES6 提供了 extends 關鍵字來支持類的繼承,這實際上依賴於 JavaScript 原有的原型繼承。子類繼承父類時,子類的原型指向父類的原型,這樣就實現了方法的繼承:

1
class Animal {
2
constructor(name) {
3
this.name = name;
4
}
5
6
speak() {
7
console.log(`${this.name} makes a noise.`);
8
}
9
}
10
11
class Dog extends Animal {
12
speak() {
13
console.log(`${this.name} barks.`);
14
}
15
}
16
17
const d = new Dog("Rex");
18
d.speak(); // "Rex barks."

這等價於 ES5 中使用原型鏈手動實現繼承:

1
function Animal(name) {
2
this.name = name;
3
}
4
5
Animal.prototype.speak = function () {
6
console.log(`${this.name} makes a noise.`);
7
};
8
9
function Dog(name) {
10
Animal.call(this, name);
11
}
12
13
Dog.prototype = Object.create(Animal.prototype);
14
Dog.prototype.constructor = Dog;
15
16
Dog.prototype.speak = function () {
17
console.log(`${this.name} barks.`);
18
};
19
20
const d = new Dog("Rex");
21
d.speak(); // "Rex barks."

5. 使用 super 調用父類方法

super 關鍵字允許子類調用父類的構造函數和方法。在子類構造函數中,super 用於調用父類的構造函數,繼承父類的屬性和方法:

1
class Animal {
2
constructor(name) {
3
this.name = name;
4
}
5
}
6
7
class Dog extends Animal {
8
constructor(name, breed) {
9
super(name); // 調用父類構造函數
10
this.breed = breed;
11
}
12
}
13
14
const d = new Dog("Rex", "Labrador");
15
console.log(d.name); // "Rex"
16
console.log(d.breed); // "Labrador"

在 ES5 中,可以通過顯式調用父類構造函數來實現類似的效果:

1
function Animal(name) {
2
this.name = name;
3
}
4
5
function Dog(name, breed) {
6
Animal.call(this, name); // 調用父類構造函數
7
this.breed = breed;
8
}
9
10
const d = new Dog("Rex", "Labrador");
11
console.log(d.name); // "Rex"
12
console.log(d.breed); // "Labrador"

總結

ES6 類的引入為 JavaScript 的面向對象編程提供了更簡潔的語法,但其底層仍然依賴於構造函數和原型鏈機制。類實際上是構造函數的語法糖,類的繼承機制也是基於原型鏈的。通過 super,子類可以調用父類的構造函數和方法,從而實現繼承關係。理解類的底層原理有助於我們在實際開發中更好地利用 JavaScript 的面向對象特性。