ES6 中的 class 是什麼?和函式構造函式差別是什麼?

2023年2月9日

💎 加入 E+ 成長計畫 與超過 250+ 位軟體工程師一起在社群中成長,並且獲得更深入、系統性軟體工程內容

ES6 class

JavaScript 在 ECMAScript 6 (ES6) 之前並沒有 class 的語法,而是會透過函式構造函式建立物件,並再通過 new 關鍵字實例。

在 ES6 時引入了 class 的概念,JavaScript class 使用的語法 class 似於其他 OOP 程式語言中的 class,但 JavaScript 的 class 是一種語法糖,本質上與其他程式語言 class 的實踐方式不一樣,JavaScript 的 class 是透過原型繼承來模擬 class 的行為。以下方程式碼為例:

class Car {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }

  drive() {
    console.log(`Driving a ${this.brand} ${this.model}`);
  }
}

const myCar = new Car("Tesla", "Model 3");
myCar.drive(); // Driving a Tesla Model 3

上方程式碼是 ES6 的寫法,但在 ES6 之前,我們會透過函式實踐相同功能

function Car(brand, model) {
  this.brand = brand;
  this.model = model;
}

Car.prototype.drive = function () {
  console.log(`Driving a ${this.brand} ${this.model}`);
};

const myCar = new Car("Tesla", "Model 3");
myCar.drive(); // Driving a Tesla Model 3

可以看到在 ES6 版本中的實踐方法,Car class 中的 drive 方法並不是在 class 內部裡封裝的方法,背後其實是賦值到了 Car 的原型 (prototype) 上而已。

ES6 class 和 ES5 函式構造函式 (constructor function) 的差別

ES6 class 和 ES5 函式構造函式 (constructor function) 主要有幾個差別:

  1. 提升 hosting :不同於函式宣告的構造函式存在提升,但使用 class 宣告則是無法再宣告前就使用。

    var newClass = new MyClass(); // Uncaught ReferenceError: MyClass is not defined is not defined
    
    class MyClass {}
    
  2. new:函式創建的構造函式如果沒有透過 new 實例化,就只是執行一般函式。但 class 構造函式則一定需要透過 new 來創建實例,否則會報錯。

    class Animal {}
    const a = Animal(); // Uncaught TypeError: Class constructor Animal cannot be invoked without 'new'
    

class 的常見方法

繼承

ES6 的 class 繼承是通過使用 extends 關鍵字實現的。

假設有一個父 class Animal

class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} eat food.`);
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

你可以創建一個子 class Dog,繼承 Animal class:

class Dog extends Animal {
  constructor(name) {
    super(name);
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

在這個例子中,Dog class 繼承了 Animal class 的所有屬性和方法,也可以重寫 speak 方法。

接著,使用 Dog class 創建物件:

const dog = new Dog("HAHA");
dog.eat(); // HAHA eat food.
dog.speak(); // HAHA barks.

static 靜態方法

靜態方法不能被物件實例繼承,只能通過 class 本身調用。這意味著,你不能通過物件實例調用 class 方法,而只能通過 class 名本身調用。

如果嘗試通過對象實例訪問靜態方法,將拋出一個錯誤,因為靜態方法不能被繼承,只能通過 class 本身訪問。

class MathHelper {
  static add(a, b) {
    return a + b;
  }
}

const math = new MathHelper();
// 嘗試通過對象實例訪問靜態方法,將拋出一個錯誤
console.log(math.add(2, 3)); // Uncaught TypeError: math.add is not a function

// 只能通過 class 本身訪問
console.log(MathHelper.add(2, 3)); // 5

Private fields

可以通過使用前綴 # 來實現 class 的私有領域 (Private fields),包括建立私有的屬性或是方法,而私有領域只能在 class 內部使用,外部無法存取。

如以下程式碼,#privateField 為私有變數,只能在 Example class 中使用,當實例 example 嘗試直接獲取 privateField 變數時,會報 SyntaxError 的錯誤。

class Example {
  #privateField = 100;

  getPrivateField() {
    return this.#privateField;
  }
}

const example = new Example();
console.log(example.getPrivateField()); // 100
console.log(example.#privateField); // SyntaxError: Private field '#privateField' must be declared in an enclosing class
🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們