JavaScript 中的類

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

我們之前也討論瞭如何使用原型建構函式來實現這樣的模型,並且 JavaScript 也提供了更接近經典面向物件概念的功能。

在本文中,我們將深入瞭解這些功能。值得記住的是,這裡描述的功能並不是一種組合物件的新方法:在底層,它們仍然使用原型。它們只是一種更輕鬆地設定原型鏈的方式。

先決條件 對 HTML 和 CSS 的基本瞭解,熟悉 JavaScript 基礎知識(參見第一步構建塊)以及 OOJS 基礎知識(參見物件簡介物件原型面向物件程式設計)。
目標 瞭解如何使用 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 中用於編寫面向物件程式的主要工具。我們在這裡沒有涵蓋所有內容,但這應該足以幫助您入門。我們的關於類的文章是學習更多內容的好地方。