JavaScript 中的類

在上篇文章中,我們介紹了一些面向物件程式設計(OOP)的基本概念,並討論了一個使用 OOP 原則模擬學校中的教授和學生的示例。

我們還討論瞭如何使用 原型建構函式 來實現這樣的模型,並且 JavaScript 也提供了更貼近經典 OOP 概念的特性。

在本文中,我們將介紹這些特性。需要注意的是,這裡描述的特性並不是組合物件的新方式:底層仍然使用原型。它們只是使設定原型鏈更容易的一種方式。

預備知識 熟悉 JavaScript 基礎(尤其是物件基礎)和本模組先前課程中涵蓋的面向物件 JavaScript 概念。
學習成果
  • 在 JavaScript 中建立類。
  • 在 JavaScript 中建立建構函式。
  • JavaScript 中的繼承和封裝。

類和建構函式

您可以使用 class 關鍵字宣告一個類。這是我們上一篇文章中 Person 類的宣告。

js
class Person {
  name;

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

  introduceSelf() {
    console.log(`Hi! I'm ${this.name}`);
  }
}

這聲明瞭一個名為 Person 的類,其中包含

  • 一個 name 屬性。
  • 一個接受 name 引數的建構函式,該引數用於初始化新物件的 name 屬性。
  • 一個 introduceSelf() 方法,該方法可以使用 this 引用物件的屬性。

name; 宣告是可選的:您可以省略它,建構函式中的 this.name = name; 行將在初始化之前建立 name 屬性。但是,在類宣告中顯式列出屬性可能會讓閱讀您程式碼的人更容易看到哪些屬性是該類的一部分。

您也可以在宣告屬性時為其設定預設值,例如 name = '';

建構函式使用 constructor 關鍵字定義。與 類定義外的建構函式 類似,它將

  • 建立一個新物件
  • this 繫結到新物件,因此您可以在建構函式程式碼中引用 this
  • 執行建構函式中的程式碼
  • 返回新物件。

給定上面的類宣告程式碼,您可以像這樣建立和使用新的 Person 例項。

js
const giles = new Person("Giles");

giles.introduceSelf(); // Hi! I'm Giles

請注意,我們使用類的名稱來呼叫建構函式,在本例中是 Person

省略建構函式

如果您不需要進行任何特殊初始化,可以省略建構函式,系統會為您生成一個預設建構函式。

js
class Animal {
  sleep() {
    console.log("zzzzzzz");
  }
}

const spot = new Animal();

spot.sleep(); // 'zzzzzzz'

繼承

使用上面的 Person 類,讓我們來定義 Professor 子類。

js
class Professor extends Person {
  teaches;

  constructor(name, teaches) {
    super(name);
    this.teaches = teaches;
  }

  introduceSelf() {
    console.log(
      `My name is ${this.name}, and I will be your ${this.teaches} professor.`,
    );
  }

  grade(paper) {
    const grade = Math.floor(Math.random() * (5 - 1) + 1);
    console.log(grade);
  }
}

我們使用 extends 關鍵字來表示該類繼承自另一個類。

Professor 類添加了一個新的 teaches 屬性,所以我們宣告它。

由於我們希望在建立新的 Professor 時設定 teaches,因此我們定義了一個建構函式,它接受 nameteaches 作為引數。此建構函式首先使用 super() 呼叫超類建構函式,並將 name 引數傳遞上去。超類建構函式負責設定 name。之後,Professor 建構函式設定 teaches 屬性。

注意:如果子類有任何自己的初始化操作,它必須首先使用 super() 呼叫超類建構函式,並將超類建構函式期望的任何引數傳遞上去。

我們還重寫了超類的 introduceSelf() 方法,並添加了一個新方法 grade() 來給論文評分(我們的教授不太好,只是給論文隨機分配分數)。

使用此宣告,我們現在可以建立和使用教授。

js
const walsh = new Professor("Walsh", "Psychology");
walsh.introduceSelf(); // 'My name is Walsh, and I will be your Psychology professor'

walsh.grade("my paper"); // some random grade

封裝

最後,讓我們看看如何在 JavaScript 中實現封裝。在上篇文章中,我們討論瞭如何希望將 Studentyear 屬性設為私有,以便我們可以更改射箭課程的規則而不破壞任何使用 Student 類的程式碼。

這是 Student 類的宣告,實現了這一點。

js
class Student extends Person {
  #year;

  constructor(name, year) {
    super(name);
    this.#year = year;
  }

  introduceSelf() {
    console.log(`Hi! I'm ${this.name}, and I'm in year ${this.#year}.`);
  }

  canStudyArchery() {
    return this.#year > 1;
  }
}

在此類宣告中,#year 是一個 私有欄位。我們可以構造一個 Student 物件,它可以在內部使用 #year,但如果物件外部的程式碼嘗試訪問 #year,瀏覽器會丟擲錯誤。

js
const summers = new Student("Summers", 2);

summers.introduceSelf(); // Hi! I'm Summers, and I'm in year 2.
summers.canStudyArchery(); // true

summers.#year; // SyntaxError

注意:在 Chrome 控制檯中執行的程式碼可以訪問類外部的私有元素。這是 DevTools 專屬的 JavaScript 語法限制放寬。

私有欄位必須在類宣告中宣告,並且它們的名稱以 # 開頭。

私有方法

除了私有欄位,您還可以擁有私有方法。與私有欄位一樣,私有方法的名稱以 # 開頭,並且只能由物件自己的方法呼叫。

js
class Example {
  somePublicMethod() {
    this.#somePrivateMethod();
  }

  #somePrivateMethod() {
    console.log("You called me?");
  }
}

const myExample = new Example();

myExample.somePublicMethod(); // 'You called me?'

myExample.#somePrivateMethod(); // SyntaxError

總結

在本文中,我們介紹了 JavaScript 中用於編寫面向物件程式的各種主要工具。我們在這裡沒有涵蓋所有內容,但這應該足以讓您入門。我們關於 的文章是一個學習更多內容的絕佳途徑。

接下來,我們將為您提供一些測試,您可以使用這些測試來檢查您對我們迄今為止提供的面向物件 JavaScript 的理解和掌握程度。