iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0

大家好,今天終於來到第十五天啦!前面介紹了很多TypeScript(TS)自己獨有的特色,像:型別推論、型別收窄等等,也介紹了程式基礎的語法:陣列、函數等等,今天則要來談談Class類別!但是,在那之前,先來了解與之相關的物件導向程式設計(OOP)。


OOP是什麼?

英文小學堂:
OOP全名:Object-Oriented Programming 物件導向程式設計

在之前介紹TS入門的文章中,時常會看到我在文章中使用物件這個詞,說到底,物件是什麼?物件可以是具體或抽象的事物,其中包含了這件事物的身分特性(屬性)和它會有的行為、功能(方法),打個比方:

將兔子視為物件,而牠的毛色、體型、個性則是能夠形容兔子這個物件的屬性,一種辨別身分的象徵。兔子會有的行為:跑、跳、跺腳、磨牙,則是物件的方法。同時物件是動態的,兔子會隨著成長而改變狀態,體型會從小變大,屬性通常用來描述物件目前的狀態,而方法則代表物件能夠執行的行為。

OOP的特色就是將物件視為單獨個體,能夠被呼叫使用,其中有三個很重要的核心概念:

  • 封裝:將物件的資料與操作資料的方法集中管理,並透過存取權限控制外部能夠如何使用這些資料。
  • 繼承:從父類別延伸出子類別,子類別擁有父類別的屬性及方法,也能夠自己新增屬性、方法。
  • 多型:指不同的物件可以透過相同的介面或方法名稱,呈現不同的行為。在使用繼承時,子類別也可以透過覆寫父類別的方法,讓同一個方法名稱產生不同的結果。

Class是什麼?

如果有點程式基礎的人,一定看過有些文章將class比喻為一張「藍圖」,它能夠描繪物件的屬性和方法、定義屬性的型別等,真正透過new建立出來的物件,則可以想像成按照這張藍圖打造出來的實例,就像我們不可能把A1藍圖折成24坪的房子,讓一對新婚夫妻入住進去,世上真有這種事我也不會在這裡寫文章了。

離題了

總之,Class類別是物件導向的基礎。

constructor建構子

當我們使用new建立Class的實例時,如果有多筆物件需要創建,不可能這樣寫:

class Animal {
	coatColor: string;
	age: number;
}

let rabbit = new Animal();
rabbit.coatColor = "white";
rabbit.age = 2;

如果我需要創十個物件,不可能這樣寫30行,如果這十筆物件的性質一樣,我們能夠用constructor簡化它:

class Animal {
	coatColor: string;
	age: number;
	//先定義這個類別需要的建構子
	constructor(coatColor: string, age: number) {
		this.coatColor = coatColor;
		this.age = age;
	} //當使用new關鍵字實例化物件時,constructor會執行大括號內的程式
}

let rabbit = new Animal("white",2); 
//創建物件時,就能順便寫好初始值了

有一點需要注意:constructor不能像一般函式一樣宣告回傳型別;建立實例時會自動執行 constructor,而它本身不會讓我們取得自訂的回傳值。

另外,可能有人會對this有疑問,以this.age = age;為範例簡單解釋一下:
左邊的this.age是「當前物件的age」,在我們創建rabbit時,指的就是rabbit的age
右邊的age則是constructor傳進來的參數。

存取修飾詞

接下來介紹常見的存取修飾詞:Public、Private和Protected
存取修飾詞是什麼?存取修飾詞決定了類別、物件、方法能不能被其他程式碼呼叫、使用的範圍,常見的有我們要介紹的這三種。

Public

Public代表屬性或方法能夠從外部直接存取,沒有任何限制。如果沒有特別寫Public的話,class的預設都是它。

class Animal {
	public coatColor: string;
	public age: number;
	constructor(coatColor: string, age: number) {
		this.coatColor = coatColor;
		this.age = age;
	}
}

let rabbit = new Animal("white",2); 	
console.log(rabbit.coatColor); //合法
console.log(rabbit.age); //合法

Private

Private只有class內部能夠直接存取,外部程式碼不能直接訪問。通常可以用來隱藏不希望外部直接操作的內部資料。

class Animal {
	public coatColor: string;
	private age: number;
	constructor(coatColor: string, age: number) {
		this.coatColor = coatColor;
		this.age = age;
	}
}

let rabbit = new Animal("white",2); 
console.log(rabbit.coatColor); //合法
console.log(rabbit.age); //因為外部沒有執行、更動這項屬性的權限,TS會報錯喔!

Protected

比起Public和Private兩個極端的範圍,Protected反而趨於保守,不像Public誰都能從外部存取,也不像Private只有內部能夠使用。一般來說,Protected不會給外部存取,但繼承它的子類別能夠存取它的屬性及方法。

class Animal {
	protected coatColor: string;
	protected age: number;
	constructor(coatColor: string, age: number) {
		this.coatColor = coatColor;
		this.age = age;
	}
}

class Rabbit extends Animal {
	birthday() {
		this.age += 1;
		console.log(`兔子過生日了!現在是 ${this.age} 歲。`);
	}
}

let rabbit = new Rabbit("white", 2);
rabbit.birthday();

readonly唯讀

如果希望資料只供檢閱,不能更動,能夠使用readonly來實現:

class Animal {
	public coatColor: string;
	readonly age: number;
	constructor(coatColor: string, age: number) {
		this.coatColor = coatColor;
		this.age = age;
	}
}

let rabbit = new Animal("white",2); 
console.log(rabbit.age); //合法,唯讀狀態可以正常讀取

rabbit.age = 3; //不合法,唯讀狀態下的屬性不能被重新賦值

readonly不代表這個值永遠不能改,而是TypeScript不允許它在初始化後再次被重新賦值。Class的readonly成員通常可以在constructor中初始化。

getter & setter

前面說了private的成員無法從外部直接存取,假設我們希望讓外部程式碼能夠取得或修改這個資料,可以使用getter和setter,並在內部控制實際資料的存取方式。

class Animal {
	private _age: number;
	constructor(age: number) {
		this._age = age;
	}
	
	get age(): number {
		return this._age;
	} // get 可以從外部取得 age 的數值
	
	set age(value: number) {
		this._age = value;
	} // set 可以從外部修改 age 的數值
}

let rabbit = new Animal(2);
console.log(rabbit.age); //輸出:2
rabbit.age = 4; //透過setter修改 _age
console.log(rabbit.age); //輸出:4

小結

class這篇是目前為止寫過最長的文章,畢竟是十分重要的物件導向觀念,在程式上會很常使用到裡面講到的概念,希望大家在看完後能夠有所收穫,今天先寫到這裡,大家拜拜!


上一篇
[Day-14]Enum列舉
下一篇
[Day-16] Generics泛型
系列文
從零開始學習TypeScript:菜鳥的入門介紹與概覽22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言