iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 25
2
Modern Web

在 React 生態圈內打滾的一年 feat. TypeScript系列 第 25

Day24 | 只要別搞混 Class,你想得到通通有

前言

本篇會介紹如何在 TypeScript 中使用 Class,但是可別搞混了哦!TypeScript 的 Class 和 JavaScript 的 Class 一點關係都沒有,雖然最後還是會編譯成 JavaScript 啦,不過編譯過後也不會是 Class,而是 Prototype 就是了,如果對 JavaScript 的 Class 還不熟的話建議可以先讀過「JavaScript | ES6 中最容易誤會的語法糖 Class - 基本用法」這篇,而在 TypeScript 的 Class 也多了一些 modifiers,例如 publicprivateprotected 本篇都會簡單介紹。


前置準備

  1. 文中的專案會以 Day23 的專案架構繼續講解,如果未跟到前一天的進度,可以從 GitHub 上 Clone 下來。
  2. 一顆擁有學習熱忱的心。

Class 類別

類別就像一個設計圖,能透過它產生一個新物件

假設我們有張製作汽車的設計圖,那只要依照這張設計圖製作,所產生出來的車都會擁有相同的特型。

在這個例子中,設計圖就是 Class 類別,而車子就是類別產生的 Instance 實體

使用方法

建立 Class 與 Instance

承上方的例子,在目錄下新增 Car.ts 檔案,並其中建立一個 Car 的 Class,在 Class 裡會描述 Instance 會有哪些特性:

class Car {
  public model: string = 'GQSM-X';

  public getDescription(): string {
    return `型號是:${this.model}`;
  }
}

遵從 Class 的淺規則,讓 Car 的字首 C 大寫,Car 內有兩個 Properties 特性,model 記錄著車子的型號,另一個是 getDescription 會回傳該車子的敘述。

使用 new 就能以 Class 創建 Instance:

const car1 = new Car();

console.log(car1.getDescription()); // 型號是:GQSM-X

不論創建幾個物件,只要是以 Car Class 建立的 instance 都會得到與上方相同的結果。

如果要執行看結果,可以在終端機中輸入 tsc Car.ts 編譯,接著使用 node Car.js 執行:

Constructor 建構子

透過 new 讓 Class 產生 Instance 的時候,可以傳入參數,而傳入的參數會被 Constructor 接收

Constructor 會在 Class 建立 Instance 時執行,且如果 Class 在建立 Instance 有傳入任何參數的話,也都會由 Constructor 接收,例如要為 Car 增加顏色,就可以使用 Constructro 接收顏色進行指定:

class Car {
  public model: string = 'GQSM-X';
  public color: string;

  constructor(color: string) {
    this.color = color;
  }

  public getDescription(): string {
    return `型號是:${this.model}(${this.color})`;
  }
}

這麼一來便能在建立 Instance 時傳入顏色,並在 Constructor 中將顏色指定給自己的 Property color

const redCar = new Car('Red');
const blueCar = new Car('Blue');

console.log(redCar.getDescription());
// 型號是:GQSM-X(Red)

console.log(blueCar.getDescription());
// 型號是:GQSM-X(Blue)

Private 私有

到目前為止,Car 內所有的屬性都是 public,如果沒有特別指定也會是 Public,也就是公開的,所以我們可以直接從 Instance 中取出 Property 的值,例如 color 或是 model

console.log(redCar.model) //GQSM-X
console.log(redCar.color) //Red

如果在 Class 的 Property 前加上 private,那 Property 就會變成私有的,無法直接以上述的方式取得值,下方就試著把 Car 內的 model 變成私有的:

class Car {
  private model: string = 'GQSM-X';

  /* 其餘省略 */
}

model 變成私有後,如果還要試圖直接從物件中取得,那就會出現警告:

Inheritance 繼承

如果要為當前的 Car 為基底,再建立另一個 Class,就能讓新的 Class 使用 Inheritance 繼承 Car 的所有 Properties,而 Inheritance 的操作也很容易,只需要在新的 Class 後使用 extends 並加上要繼承的 Class 就好,例如:

class Car {
  private model: string = 'GQSM-X';
  public color: string;

  constructor(color: string) {
    this.color = color;
  }

  public getDescription(): string {
    return `型號是:${this.model}(${this.color})`;
  }
}

// CarII 繼承了 Car
class CarII extends Car {
  public getDescription(): string {
    return `${super.getDescription()}(II)`;
  }
}

這裡需要注意,雖然 CarII 沒有 Constructor,但是因為它繼承了 Car,所以還是需要在建構 Instance 的時候傳進參數作為 color,而在 getDescription 內使用的 super 就代表著父類別,也就是執行了父類別的 getDescription

除此之外,如果父類別的 Constructor 需要接收多個值,那在子類別的 Constructor 中,也可以用 super 將值傳送給父類別的 Constructor,但如果父類別只需要接收一個參數,那就不需要再使用 super,TypeScript 會自動將它送給父類別。

上方的執行結果如下:

const redCarII = new CarII('Red');
console.log(redCarII.getDescription());;

// 執行結果:
// 型號是:GQSM-X(Red)(II)

Protected 保護

Protected 和 Private 很類似,差別在 Private 是私有的屬性,因此除了本身的類別外,便無法偷看裡面是什麼,否則就會出錯,子類別也不例外,但是如果 Protected,就還可以在子類別中讀取:

class Car {
  // 將 model 改為 protected
  protected model: string = 'GQSM-X';
  
  /* 其餘省略 */
}

// CarII 繼承了 Car
class CarII extends Car {
  public getModel(): string {
    // 直接使用父類別的 protected 
    return this.model;
  }
  public getDescription(): string {
    return `${super.getDescription()}(II)`;
  }
}

console.log(redCarII.getModel());
// GQSM-X

這裡特別說明一下,關於獲取 model 這個屬性為什麼是使用 this 而不是 super

因為在 JavaScript 裡,Method 方法會存放於 Prototype,一般的值例如 model 則是實現於 Instance,

super 正是指向父類別的 Prototype,而不是 Instance,因此在 Prototype 中找不到 model 時就會發生警告,另一方面的 this 就是指本身被創建出來的 Instance,既然是 Instance 那當然就找得到 model

Static 靜態

正常來說,Class 都要被創建為 Instance 後,才有辦法使用在 Class 內所描述的方法,但只要使用 static,就能直接在 Class 上呼叫此方法:

// CarII 繼承了 Car
class CarII extends Car {
  static getComment(): string {
    return '我是二代車';
  }

  /* 其餘省略 */
}

// 直接呼叫靜態 Method
console.log(CarII.getComment());

使用 static 時需要注意一下,因為不需要建立 Instance 就可以使用,所以在 static 內是不能使用 this 的,因為根本就沒有 Instance。

本文的範例程式碼會提供在 GitHub 上,歡迎各位參考:)


結尾

本章介紹了在 TypeScript 中使用 Class 的用法,雖然有些語法還沒支援,但是看起來和 JavaScript 的 ES6 還挺相似的,下一章會接著說明關於 Interface 接口,在物件導向的編程中,Interface 可是佔了很重要的部分!

如果文章中有任何問題,或是不理解的地方,都可以留言告訴我!謝謝大家!


上一篇
Day23 | 你説 JS 是什麼弱型別? TypeScript 強勢登場
下一篇
Day25 | 善用 interface 抽象概念,為 Class 找到出路
系列文
在 React 生態圈內打滾的一年 feat. TypeScript31

1 則留言

1
連城
iT邦新手 5 級 ‧ 2020-07-14 18:08:39

const redCarII = new CarII('Red');
redCarII.getDescription();

改成

const redCarII = new CarII('Red');
console.log(redCarII.getDescription());

其實我一直都不是很理解OOP的結構
但是這篇真的讓我有多了解一點OOP
甚麼時候該用super
甚麼時候該用this
都有很好的解釋

感謝你!我保證今年不會再寫 React 的系列文了!

連城 iT邦新手 5 級 ‧ 2020-07-22 14:01:20 檢舉

哈哈 我現在半隻腳踏入TypeScript
也是要感謝你的文章
期待你今年鐵人賽

PS 我私心希望會有react刻畫面教學
沒有也沒關係~我會再爬文學的

我要留言

立即登入留言