JavaScript >> Javascript 文檔 >  >> Tags >> class

JavaScript 類理解指南

簡介

當你想到 面向對象編程 作為一種範式,JavaScript 可能不是第一個想到的語言。

在本指南中,我們將嘗試將 JavaScript 進一步推到關聯列表的前面,討論如何應用面向對象的原則 在編寫 JavaScript 代碼時。值得注意的是,我們將介紹的一些功能仍在開發中,但大多數都在生產中並且功能齊全。我們將在指南發佈時適當地更新它們。

由於 JavaScript 主要在 Web 上使用,因此將 OOP 應用於它可能非常有用,例如,從服務器獲取數據(例如,來自 MongoDB 數據庫的集合)可以在具有屬性的類中形成,因為它讓數據操作更直觀、更輕鬆。

什麼是面向對象編程(OOP)?

在開始之前,讓我們先了解一下 OOP 的定義和一些基本原則。如果你已經熟悉了這些概念,可以直接跳到用 JavaScript 創建一個類。

類和屬性

假設我們有一個非常簡單的類,叫做 08 有兩個屬性 - 1324 ,兩者都是字符串。這是我們製作對象的藍圖。此類的對象將具有屬性和值,例如 3345 .

為了讓我們能夠從一個特定的類中創建這樣的對象,該類必須包含一個構造方法 - 或者很快,一個構造函數 .構造函數實際上是一本關於如何實例化對象和賦值的手冊 .創建構造函數最常見的做法是將其命名為與類相同,但並非必須如此。

例如,對於我們的 56 類,我們定義一個 69 定義如何的構造函數 我們在實例化類時為類中的屬性賦值。它通常接受 73 用作屬性值的參數:

class ProgrammingLanguage {
    // Attributes
    String name;
    String founder;
    
    // Constructor method
    ProgrammingLanguage(string passedName, string passedFounder){
       name = passedName;
       founder = passedFounder;
    }
}

注意: 雖然類似,但這不是 JavaScript 代碼,僅用於說明目的。我們將在創建類時使用 JavaScript。

然後,在實例化這個類時,我們將一些參數傳遞給構造函數,調用 83 對象:

ProgrammingLanguage js = new ProgrammingLanguage("JavaScript", "Brendan Eich");

這將創建一個 95 類型的對象 js 帶有屬性 107116 .

Getter 和 Setter 方法

OOP 中還有另一組關鍵方法 - getterssetter .顧名思義,一個 getter 方法獲取一些值,而 setter 設置它們。

在 OOP 中,它們用於從對像中檢索屬性,而不是直接訪問它們,以封裝它們、執行潛在的檢查等。Setter 用於將對象的屬性設置為給定的值——同樣,在封裝和隔離中方式。

注意: 為了真正限制這種訪問,屬性通常設置為 122 (類外不可訪問),當相關語言支持訪問修飾符時。

例如,如果您想將某人的年齡設置為 135,您可能會被阻止 通過setter ,如果允許您直接訪問屬性,則無法強制執行。

如果您使用 empty,Setter 可用於更新值或初始設置它 構造函數 - 即最初不設置任何值的構造函數。

命名 getter 和 setter 的約定是它們應該以 148 為前綴 或 152 ,後跟他們正在處理的屬性:

getName() {
    return name;
}

setName(newName) {
    name = newName;
}

這個 關鍵字

類是自我意識的 . 166 關鍵字用於引用這個實例 在一個類中,一旦它被實例化。您只會在引用自身的類中使用關鍵字。

例如,在之前的構造函數中,我們使用了傳遞的變量 175183 ,但如果這些只是 199200 哪個更有意義?

我們的構造函數看起來像:

ProgrammingLanguage(String name, String founder) {
    name = name;
    founder = founder;
}

那麼,哪一個 211 我們是否設置為哪個 220 ?我們是將傳遞的值設置為屬性還是相反?

這是 231 關鍵字開始:

ProgrammingLanguage(String name, String name) {
       this.name = name;
       this.founder = founder;
}

現在,很明顯我們正在設置 this class' 屬性的值 到構造函數傳遞的值。

同樣的邏輯也適用於我們的 getter 和 setter:

getName() {
	return this.name;
}

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

我們正在獲取和設置 name 這個類 .

屬性和構造函數的語法以及大小寫約定因語言而異,但 OOP 的主要原則保持不變。

鑑於構造函數、getter 和 setter 的標準化程度,現在大多數 IDE 都集成了用於創建構造函數方法以及 getter 和 setter 的快捷方式。您需要做的就是定義屬性並通過 IDE 中的相應快捷方式生成它們。

現在我們已經更熟悉了 OOP 的概念,我們可以深入了解 JavaScript 中的 OOP。

在 JavaScript 中創建類

注意: JavaScript 帶來的一個區別是,在定義類時 - 您不必明確說明它具有哪些屬性/字段。它更加靈活,同一類的對象可以有不同的字段 如果你願意的話。再說一次,這是不鼓勵的,因為它確實違反了 OOP 原則,並且標準化的做法部分是通過有一個構造函數來強制執行的,你可以在其中設置所有屬性(因此,有某種屬性列表)。

在 JavaScript 中,有兩種​​方法可以創建一個類:使用 類聲明 並使用類表達式 .

使用類聲明 ,通過 248 關鍵字,我們可以在大括號中定義一個類及其所有屬性和方法:

class Athlete {}

這些可以在各自的文件中定義,也可以在另一個文件中與其他代碼一起定義為便利類。

或者,使用 類表達式 (已命名或未命名)讓您可以內聯定義和創建它們:

// Named
let Athelete = class Athlete{}
   
// Unnamed
let Athlete = class {}
   
// Retrieving the name attribute
console.log(Athlete.name);

不建議像這樣檢索屬性,因為真正的 OOP 精神 - 我們不應該直接訪問類的屬性。

由於我們沒有構造函數,也沒有 getter 和 setter,讓我們繼續定義它們。

在 JavaScript 中創建構造函數、Getter 和 Setter

另一件需要注意的事情是 JavaScript enforces 構造函數的名稱。它必須被命名為 257 .這也是您本質上定義類屬性的地方,儘管它比 Java 等語言更隱含:

class Athlete{
	constructor(name, height, weight){
        this._name = name;
        this._height = height;
        this._weight = weight;
    }
}

const athlete = new Athlete("Michael Jordan", 198, 98);

如果您想預先定義屬性,可以 但鑑於 JavaScript 的性質,它是多餘的,除非您嘗試創建私有屬性。在任何情況下,您都應該在屬性名稱前加上 268 .

由於 JavaScript 不支持開箱即用的封裝,因此這是一種告訴您的類的用戶不要的方法 直接訪問屬性。如果您在屬性名稱前看到下劃線 - 請幫您自己和類的創建者一個忙,不要直接訪問它。

注意: 技術上可行 在 JavaScript 類中生成私有屬性,但它並未被廣泛採用或使用 - Douglas Crockford 建議將變量隱藏在閉包中以實現此效果。

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

您可以通過 274 進一步註釋您的意圖 註釋,表示您希望屬性具有的訪問級別:

class Athlete {
    /** @access private */
   _name;
    
    constructor(name){
        this._name = name;
    }
    
    getName() {
        return this._name;
    }
    
    setName(name) {
        this._name = name;
    }
}

然後你可以實例化一個對象,以及獲取和設置它的屬性:

var athlete = new Athlete('Michael Jordan');
console.log(athlete.getName());

athlete.setName('Kobe Bryant');
console.log(athlete.getName());

這導致:

Michael Jordan
Kobe Bryant

不過,您也可以直接訪問該屬性:

console.log(athlete._name); // Michael Jordan

將字段設置為私有

最後,私有字段 被引入,並以 287 為前綴 .他們實際上強製字段的使用是私有的,他們不能 在類之外訪問 - 只能通過公開它的方法:

class Athlete {
    /** @access private */
    #name;
    
    constructor(name){
        this.#name = name;
    }
    
    getName() {
        return this.#name;
    }
    
    setName(name) {
        this.#name = name;
    }
}

var athlete = new Athlete('Michael Jordan');
console.log(athlete.getName()); // Michael Jordan
console.log(athlete.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class

這樣,實際上實現了封裝,因為用戶只能通過可以驗證返回值的經過審查的方法訪問屬性,或者阻止它們設置意外值,例如將數字而不是字符串分配給 297 屬性。

注意: 要將屬性標記為私有,您必須在 getter 和 setter 之前聲明它。此功能自 2018 年(Babel 7.0+)以來一直存在,但它可能不適用於某些較舊的環境。

get設置 關鍵字

或者,JavaScript 有一組特殊的關鍵字 - 300310 ,可以用來製作getter和setter。使用時,它們綁定 當您想訪問它們時調用的函數的某些屬性。

在屬性和由 321 綁定的 getter/setter 方法之間使用相同的名稱是慣例 和 339 , 沒有 一個前綴(它會是多餘的):

class Athlete {

	constructor(name) {
        this._name = name;
    }
	
    get name() {
	    return this._name;
    }
    
    set name(name){
        this._name = name;
    }
}

var athlete = new Athlete("Michael Jordan");

console.log(athlete.name); // Output: Michael Jordan

athlete.name = "Kobe Bryant";
console.log(athlete.name); // Output: Kobe Bryant

雖然看起來很像,但我們不是 訪問 344 直接屬性。我們隱式調用 357 方法,通過嘗試 訪問屬性,當該請求被重新路由到 366 方法。為了更清楚,讓我們修改 373 方法體:

get name() {
    return "Name: " + this._name;
}

現在,這個:

var athlete = new Athlete('Michael Jordan')
console.log(athlete.name);

結果:

Name: Michael Jordan

注意: 添加下劃線的另一個原因 (389 ) 屬性名稱是如果您將使用這種方法來定義 getter 和 setter。如果我們只使用 399 作為屬性,它會是模棱兩可的,因為 403 也可以參考419 .

當我們嘗試實例化類時,這將啟動一個遞歸循環,填充調用堆棧直到它耗盡內存:

class Athlete {
    constructor(name) {
        this.name = name;
    }
  
    get name() {
        return this.name;
	}
    
    set name(name) {
        this.name = name;
    }
}

var athlete = new Athlete('Michael Jordan');
console.log(athlete.name);

結果是:

script.js:12
        this.name = name;
                  ^

RangeError: Maximum call stack size exceeded

使用 Getter/Setter 函數或關鍵字?

社區在這些之間的選擇上存在分歧,一些開發人員更喜歡其中一個。沒有明顯的贏家,兩種方法都通過允許封裝來支持 OOP 原則,並且可以返回和設置私有屬性。

定義類方法

我們之前已經定義了一些方法,即 getter 和 setter 方法。以同樣的方式,我們可以定義執行其他任務的其他方法。

定義方法有兩種主要方式 - 在類中課外 .

到目前為止,我們一直在使用類內定義:

class Athlete {
 // Constructor, getters, setters
 
    sayHello(){
        return "Hello, my name is " + this.name;
    }
}
console.log(athlete.sayHello()) // Hello, my name is Kobe Bryant

或者,您可以在類之外通過函數聲明顯式創建函數:

class Athlete {
    // Class code
}

athlete.sayHello = function(){
    return "Hello, my name is " + athlete.name;
}

var athlete = new Athlete("Kobe Bryant");
console.log(athlete.sayHello()) // Output: Hello, my name is Kobe Bryant

對 JavaScript 來說,這兩種方法都是一樣的,所以你可以選擇更適合你的一種。

JavaScript 中的類繼承

OOP 的一個關鍵概念是類繼承 .一個子類 (子類)可以擴展 從一個類中定義新的屬性和方法,同時繼承 一些來自它的超類 (父類)。

一個 420 可以是 432 , 444459 但所有這三個都是 461 的實例 .

在 JavaScript 中,479 關鍵字用於創建子類:

// Athlete class definition

class BasketballPlayer extends Athlete {
    constructor(name, height, weight, sport, teamName){
        super(name, height, weight);
		this._sport = sport;
        this._teamName = teamName;
    }
    
    get sport(){
        return this._sport;
    }
    
    get teamName(){
        return this._teamName;
    }
}

const bp = new BasketballPlayer("LeBron James", 208, 108, "Basketball", "Los Angeles Lakers");

我們創建了 486 的對象 包含 491 中使用的屬性的類 類,以及兩個新屬性 506515 - 特定於 521 類。

類似於 537這個類 , 540 指超類。通過調用 558 使用參數,我們調用超類的構造函數,設置一些屬性,然後設置特定於 569 的新屬性 類。

當我們使用 574 關鍵字,我們繼承了超類中存在的所有方法和屬性 - 這意味著我們繼承了 589 方法、getter 和 setter 以及所有屬性。我們可以使用該方法創建一個新方法並向其添加更多方法,如下所示:

class BasketballPlayer extends Athlete{
	// ... previous code
	
	fullIntroduction(){
		return this.sayHello() + " and I play " + this.sport + " in " + this.teamName;
	}
}

const bp = new BasketballPlayer("LeBron James", 208, 108, "Basketball", "Los Angeles Lakers");
console.log(bp.fullIntroduction());

這將導致:

Hello, my name is LeBron James and I play Basketball in Los Angeles Lakers

注意: 我們還沒有定義 591 600 中的方法 類,但仍然可以通過 613 訪問它 .怎麼會這樣?它不是 623 的一部分嗎 班級?這是。但是637 繼承了這個方法 所以它和 641 中定義的一樣好 類。

instanceof 運算符

656 運算符用於檢查某個對像是否為 instance of 某一類。返回類型是 664

var bp = new BasketballPlayer();
var athlete = new Athlete();

console.log(bp instanceof BasketballPlayer); // Output: true
console.log(bp instanceof Athlete); // Output: true

console.log(athlete instanceof Athlete); // Output: true
console.log(athlete instanceof BasketballPlayer); // Output: false

一個 674 是一個 686 所以 699 是兩者的一個例子。另一方面,702 不必是 716 ,所以 724 只是 736 的一個實例 .如果我們實例化 740 作為一名籃球運動員 ,如 759 ,它們是兩者的實例。

結論

在本指南中,我們了解了 OOP 的一些基本原則以及類在 JavaScript 中的工作方式。 JavaScript 還不完全適合 OOP,但正在朝著進一步調整功能邁進。

我們探索了類定義、屬性、getter、setter、封裝、類方法和繼承。


Tutorial JavaScript 教程
  1. 我的第一個啤酒節

  2. 使用 Storybook 管理設計令牌

  3. 如何通過擴展修改 chrome 中的當前 url 位置

  4. 使用 JavaScript 升級! 8 級

  5. 如何在 3 分鐘內創建一堆 Google 快訊?

  6. 我怎樣才能使只有數字可以寫在框中?限制為 6 位數

  7. JavaScript 函數分解

  1. React 綁定模式:處理 `this` 的 5 種方法

  2. 使用網絡信息 API 檢查網絡狀態

  3. Kubernetes 和 Node.js 上的水平擴展 WebSockets

  4. 如何在 JavaScript 中避免 if else

  5. JavaScript。記憶。架構和生命週期。

  6. Vite - 沒有 Vuejs、React 和 Preact 的捆綁設置

  7. 承諾你的異步操作

  1. 在 Vue.js 中為 Pinterest 板創建幻燈片

  2. 我幾乎總是在 Javascript 中使用 For 循環

  3. 如何將內容添加到您的身體

  4. Windows 11,但適用於 Web