類別封裝是一種將類別的內部細節隱藏起來,確保類別的屬性和方法在外部程式碼中不被隨意訪問,提高程式碼的可維護性和可讀性,同時提供了一定程度的安全性。
類別修飾符是指定類別的屬性和方法是否有對外使用權限的關鍵字。它讓我們控制哪些屬性和方法只能在類別內部使用,哪些可以在外部使用,以及哪些可以被子類別繼承。
下面是 TypeScript 中的主要修飾符:
預設
的修飾符,表示在不寫任何修飾符的情況下,都將代表著 public 修飾符。類別公開的屬性和方法可以在外部中被使用
。
看以下範例:
class Human {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Person extends Human {
getName(): void {
console.log(this.name); // 可以直接使用父類別的 name 屬性
}
}
const person = new Person("威爾豬");
person.getName(); // 輸出:威爾豬
console.log(person.name); // 輸出:威爾豬 ⭕️ 外部可以使用 name 屬性
在這個例子中,name 屬性使用了預設修飾符 ( public ),因此可以在類別外部直接使用。
我們也可以使用 **參數屬性**
來簡化參數賦值給類別屬性的動作。因為在建構函式中將參數賦值給屬性需要額外的程式碼,使用參數屬性可以減少這種重複性的工作。
class Human {
// 參數屬性自動將參數值賦值給對應的屬性
constructor(public name: string) {}
}
class Person extends Human {
getName(): void {
console.log(this.name);
}
}
const person = new Person("威爾豬");
person.getName(); // 輸出:威爾豬
console.log(person.name); // 輸出:威爾豬
注意:public 修飾符就必須要寫出,不然會 TypeScript 會報錯,導致編譯出來的結果為 undefined。
私有的屬性和方法只能在該類別內部使用
,外部無法使用。
看以下範例:
class Animal {
private _name: string;
constructor(name: string) {
this._name = name;
}
}
class Cat extends Animal {
showName(): void {
console.log(`這隻貓貓的名字是${this._name}`); // ❌ 無法使用父類別的私有屬性
}
}
在這個例子中,Animal 類別的 name 屬性被聲明為私有屬性,只能在該類別內部訪問。Cat 類別繼承了 Animal 並試圖訪問父類別的私有屬性,這將導致編譯錯誤。
我們看一下 MDN 的一段話:
這段話表示我們可以將 private 改成在前綴使用 #
來代替。
順便修改一下上面的範例讓錯誤消失:
class Cat {
// 修改 private 為 # 符號
#name: string;
constructor(name: string) {
this.#name = name;
}
showName(): void {
console.log(`這隻貓貓的名字是${this.#name}`);
}
}
const myCat = new Cat("旺妞");
myCat.showName(); //輸出: 這隻貓貓的名字是旺妞
當然我們也可以使用 參數屬性
就可以了:
class Cat {
constructor(private _name: string) {}
showName(): void {
console.log(`這隻貓貓的名字是${this._name}`);
}
}
const myCat = new Cat("旺妞");
myCat.showName(); //輸出: 這隻貓貓的名字是旺妞
protected 與 private 相似,但有一個重要的區別,受 保護的屬性和方法可以在類別內部和繼承該類別的子類別中使用
。
看以下範例:
class Car {
constructor(protected speed: number) {}
addSpeed(amount: number) {
this.speed += amount;
}
}
class Bus extends Car {
nowSpeed() {
console.log(`現在時速 ${this.speed}`); // 可以使用父類別的 speed 屬性
}
}
const bus = new Bus(60);
bus.nowSpeed(); // 輸出: 現在時速 60
bus.addSpeed(20);
bus.nowSpeed(); // 輸出: 現在時速 80
console.log(bus.speed); // ❌ 外部不能使用 speed 屬性
在這個例子中,speed 屬性使用了 protected 修飾符,所以它可以在 Car 類別的子類別 Bus 中使用,但在外部是無法使用的。
用於將類別的屬性和方法設置為只能讀取,一旦初始化後就不能再被修改。
看以下範例:
class Car {
constructor(readonly brand: string, readonly model: string) {}
}
const myCar = new Car("Ferrari", "LaFerrari");
myCar.brand = "Bugatti"; // ❌ 無法修改 readonly 屬性
brand 和 model 屬性使用了 readonly 修飾符,所以只能讀取,不能再進行修改。
不過要注意的是,修飾符在 TypeScript 中主要是用來進行
靜態類型檢查
,而不是在運行時實施的嚴格封裝。所以即便使用了 private、protected、readonly 等存取修飾符的屬性或方法,雖然 Typescript 會跳出錯誤警告,但它還是會正常編譯的。小夥伴們應該還記得如何讓 TypeScript 報錯就不進行編譯吧。( 忘記了就來看配置文件吧! )
類別修飾符用於修改類別本身的特性,並不影響類別屬性和方法的可見性或使用權限。
用於聲明抽象類別或抽象方法,不允許實體化,但可以被其他類別來繼承,通常用於 定義共用的屬性和方法
。( 在 類別 章節有說明。 )
用於定義類別的靜態屬性和方法,這些屬性和方法屬於類別本身,可以在類別的內部直接使用,不需要另外創建類別的實體 / 實例 (Instance)。
看以下範例:
class Me {
static firstName: string;
static add(lastName: string): string {
return this.firstName + lastName;
}
}
Me.firstName = "威爾";
console.log(Me.add("豬")); // 輸出: 威爾豬
在這個例子中,我們定義了一個 Me 類別,包含一個靜態屬性 firstName 和一個靜態方法 add,它們可以直接通過類別名稱訪問,不需要再創建類別的實體。
當然我們還可以加上存取修飾符:
class Me {
// 改成靜態私有屬性
static #firstName: string;
static add(lastName: string): string {
return this.#firstName + lastName;
}
}
Me.#firstName = "威爾"; // ❌ 無法在外部使用私有屬性
用於將類別或模組 ( 如函式、變數等 ) 匯出,以便在其他檔案中引入 ( import ) 使用。
// Person.ts
export class Person {
constructor(private _name: string, private _age: number) {}
greet(): string {
return `我是${this._name},今年 ${this._age} 歲`;
}
}
// app.ts
import { Person } from "./Person";
const person1 = new Person("威爾豬", 3);
const person2 = new Person("威爾羊", 5);
console.log(person1.greet()); // 輸出: 我是威爾豬,今年 3 歲
console.log(person2.greet()); // 輸出: 我是威爾羊,今年 5 歲
在這個範例中,我們使用 export 將 Person 類別匯出,然後在 app.ts 中使用 import 將它導入。這樣就可以在不同檔案中使用相同的類別了。
Getters 和 Setters 可以讓我們在取得或設置屬性值時,執行自定義的邏輯
,而不僅僅是直接使用或單純設定屬性而已。
看以下範例:
class Person {
constructor(private _name: string) {}
get name(): string {
return this._name;
}
set name(value: string) {
value.trim() === "" ? console.log("名字不能為空") : (this._name = value);
}
getName(): string {
return `我是${this._name}`;
}
}
const person = new Person("威爾豬");
// 使用 getter 取得屬性
console.log(person.name); // 輸出: 威爾豬
// 使用 setter 設定屬性
person.name = ""; // 輸出: 名字不能為空
console.log(person.getName()); // 輸出: 我是威爾豬
這個範例中,我們創建了一個 Person 類別,其中有一個私有屬性 _name。當我們使用 person.name 時,實際上是調用了 getter 方法,而在將 person.name 賦予新值時,實際上是調用了 setter 方法。
我們可以使用上面的存取修飾符、類別修飾符和 Getter / Setter 等來設置類別裡屬性和方法的使用權限,將類別的內部細節隱藏起來,只向外部公開必要的接口。通過合理運用這些修飾符,我們可以創建出結構化、安全性高且易於維護的程式碼,同時保證了類別裡屬性和方法的使用和修改受到適當的限制。