0%

重新學習TS-五-TypeScript-Class

前言

今天來研究研究 TS 中 Class 的細節

gif


Class 是什麼?

TypeScript(TS) 中,class用於定義物件導向程式設計中的類別。與 JavaScript 相比,TypeScriptclass 具有一些特別的功能和優點

  • 靜態類型檢查
  • 封裝和訪問修飾詞
  • 建構函式和初始化器
  • 繼承和多型
  • 抽象類別和介面
  • 存取器(Accessors)
特點 說明
靜態類型檢查 TypeScript 提供強大的型別檢查,可在類別的屬性方法建構函式上聲明型別,以確保程式碼的正確性。
封裝和訪問修飾詞 使用訪問修飾詞(publicprivateprotected)來控制屬性和方法的可訪問性,確保只有適當的程式碼可以訪問和修改類別的內部狀態。
建構函式和初始化器 類別中的建構函式(constructor)在建立類別實例時被調用,可用於初始化屬性或執行其他必要的操作。
繼承和多型 類別可以透過繼承機制建立繼承關係,子類別可以繼承父類別的屬性和方法,並進行擴充和覆寫,提供程式碼的重用和組織的靈活性。
抽象類別和介面 TypeScript 支援抽象類別和介面的定義。抽象類別不能被實例化,但可以作為其他類別的基類,而介面定義了一組合約,類別可以實現這些介面以確保符合特定的結構。
存取器(Accessors) 使用 getset 存取器來設置和獲取類別的屬性值,可對屬性的存取進行更多控制,例如執行額外的邏輯驗證

TypeScript 和 JavaScript 中 class 的差異

功能 TypeScript (TS) JavaScript (JS)
靜態型別檢查 支援靜態型別檢查,可在編譯時檢測並捕獲許多錯誤。 沒有內建的靜態型別檢查機制。
型別註解和型別推斷 可以聲明變數、函式和物件的型別,並且進行型別推斷。 可以聲明變數的型別,但沒有內建的型別推斷機制。
類別和模組 支援類別和模組的原生語法,可使用 classmodule 關鍵字。 沒有內建的類別和模組語法,使用原型和函式進行物件導向編程。
介面和抽象類別 支援介面和抽象類別的定義,用於描述物件的結構和行為。 沒有內建的介面和抽象類別語法,通常使用原型和函式來實現相似的功能。
可選參數和預設參數 可以將函式的參數設置為可選的 ? 符號標記,並指定預設值。 可以模擬可選參數和預設參數,但沒有內建的語法支援。
存取修飾詞 支援存取修飾詞(publicprivateprotected)來控制屬性和方法的訪問權限。 沒有內建的存取修飾詞,所有屬性和方法都是公開的。
繼承和多型 支援類別之間的繼承關係,子類別可以繼承父類別的屬性和方法。 支援原型鏈繼承,但沒有內建的語法支援類別之間的繼承。
建構函式和初始化器 可以在類別中定義建構函式(constructor),用於初始化

使用方式

定義類的關鍵字為class,後面緊跟類名,Class 可以包含以下幾個模塊(Class 的數據成員):

  • 字段: 字段是類裡面聲明的變量。字段表示對象的有關數據。
  • 構造函數: 類實例化時調用,可以為類的對象分配內存。
  • 方法: 方法為對像要執行的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Car {
// 字段
engine:string;

// 構造函數
constructor(engine:string) {
this.engine = engine
}

// 方法
disp():void {
console.log("發動機是 : "+this.engine)
}
}

繼承

Class的繼承使用過extends的關鍵字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}

class Dog extends Animal {
bark() {
console.log('Woof! Woof!');
}
}

const dog = new Dog();
dog.bark();
dog.move(10);
dog.bark();

Dog是一個派生類,它派生自 Animal 基類,派生類通常被稱作子類,基類通常被稱作超類

Dog類繼承了 Animal 類,因此實例dog也能夠使用 Animal 類 move 方法

同樣,類繼承後,子類可以對父類的方法重新定義,這個過程稱之為方法的重寫,通過 super 關鍵字是對父類的直接引用,該關鍵字可以引用父類的屬性和方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
class PrinterClass {
doPrint():void {
console.log("父類的 doPrint() 方法。")
}
}

class StringPrinter extends PrinterClass {
doPrint():void {
super.doPrint() // 调用父類的函数
console.log("子類的 doPrint()方法。")
}
}

修飾詞

可以看到,上述的形式跟ES6十分的相似,typescript在此基礎上添加了三種修飾詞 :

  • 公共public:可以自由的訪問類程序裡定義的成員
  • 私有private:只能夠在該類的內部進行訪問
  • 受保護protect:除了在該類的內部可以訪問,還可以在子類中仍然可以訪問

私有修飾詞

只能夠在該類的內部進行訪問,實例對象並不能夠訪問

pic

並且繼承該類的子類並不能訪問,如下圖所示:

pic

受保護修飾詞

跟私有修飾詞很相似,實例對象同樣不能訪問受保護的屬性,如下:

pic

有一點不同的是 protected 成員在子類中仍然可以訪問

piC

除了上述修飾詞外,還有唯讀修飾詞

唯讀修飾詞

通過readonly關鍵字進行聲明,唯讀屬性必須在聲明時構造函數里被初始化,如下:

pix


除了實體屬性 還有靜態屬性

靜態屬性

這些屬性存在於Class本身上面而不是Class的實例上,通過static進行定義,訪問這些屬性需要通過類型.靜態屬性的這種形式訪問,如下所示:

1
2
3
4
5
class Square {
static width = '100px'
}

console.log(Square.width) // 100px

上述的Class都能發現一個特點就是,都能夠被實例化,在 typescript中,還存在一種抽像類

抽像類

抽像類做為其它派生類的基類使用,它們一般不會直接被實例化,不同於接口,抽像類可以包含成員的實現細節

abstract關鍵字是用於定義抽像類和在抽像類內部定義抽象方法,如下所示:

1
2
3
4
5
6
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}

這種類並不能被實例化,通常需要我們創建子類去繼承,如下:

1
2
3
4
5
6
7
8
9
10
11
class Cat extends Animal {

makeSound() {
console.log('miao miao')
}
}

const cat = new Cat()

cat.makeSound() // miao miao
cat.move() // roaming the earch...

應用場景

除了日常藉助類的特性完成日常業務代碼,還可以將類(class)也可以作為接口,尤其在 React 中是很常用的,如下:

1
export default class Carousel extends React.Component<Props, State> {}

由於組件需要傳入 props 的類型Props,同時有需要設置默認 propsdefaultProps,這時候更加適合使用class作為接口

先聲明一個類,這個類包含組件 props 所需的類型和初始值:

1
2
3
4
5
6
7
8
9
10
11
12
13
// props的类型
export default class Props {
public children: Array<React.ReactElement<any>> | React.ReactElement<any> | never[] = []
public speed: number = 500
public height: number = 160
public animation: string = 'easeInOutQuad'
public isAuto: boolean = true
public autoPlayInterval: number = 4500
public afterChange: () => {}
public beforeChange: () => {}
public selesctedColor: string
public showDots: boolean = true
}

當我們需要傳入 props 類型的時候直接將 Props 作為接口傳入,此時 Props 的作用就是接口,而當需要我們設置defaultProps初始值的時候,我們只需要:

1
public static defaultProps = new Props()

Props的實例就是 defaultProps 的初始值,

這就是 `class` 作為接口的實際應用,
我們用一個 `class` 起到了接口和設置初始值兩個作用,
方便統一管理,減少了代碼量

References

你的支持是我活力的來源 :)