類是對對象的抽象,描述了對象的特征和行為,而對象就是類的實例。ES6引入了類的概念(相關內容可參考ES類和ES6類的繼承兩節),TypeScript在此基礎上,不僅根據ES7等規范完善了類的語法,還添加了許多其它語法。而在使用TypeScript的類時,不必關心兼容性問題,因為這些工作已由編譯器完成。
下面是一個簡單的類,包含3個成員:帶private修飾符的name屬性、構造函數constructor()和getName()方法,最后一句使用new運算符創建了Person類的實例,並調用了一次它的構造函數。
class Person { private name: string; constructor(name: string) { this.name = name; } getName() { return this.name; } } let worker = new Person("strick");
編譯后的代碼如下所示,通過傳統的構造函數和基於原型的繼承來模擬一個類。
var Person = /** @class */ (function() { function Person(name) { this.name = name; } Person.prototype.getName = function() { return this.name; }; return Person; })(); var worker = new Person("strick");
一、屬性
在ES6中,實例屬性(即自有屬性)得作為this對象的屬性存在,並且一般都會在構造函數中執行初始化,而TypeScript允許在類中直接定義實例屬性,如下所示。
class Person { constructor(name: string) { this.name = name; } } //TypeScript中的實例屬性 class Person { name: string; }
不僅如此,TypeScript還提供了存在於類本身上的靜態屬性,即不需要實例化就能調用的屬性。在下面的示例中,為age屬性添加了static關鍵字,使其成為靜態屬性,通過類的名稱就能直接調用它。
class Person { static age: number; } Person.age = 28;
二、修飾符
修飾符是用於限定成員或類型的一種符號,TypeScript包含三個訪問修飾符:public、private和protected,以及一個成員修飾符:readonly。
1)public
在TypeScript中,成員默認都是public的,即在派生類(也叫子類)或類的外部都能被訪問。在下面的示例中,Person類中的name屬性是公共的,Programmer類繼承了Person類。注意,當派生類包含一個構造函數時,必須調用super()方法,執行基類(即父類)的構造函數,並且該方法得在訪問this對象之前調用。
class Person { public name: string; constructor(name: string) { this.name = name; } } class Programmer extends Person { constructor(name: string) { super(name); } }
在初始化Person類或Programmer類之后,就能通過創建的實例來訪問name屬性,如下所示。
let person = new Person("strick"); person.name; //"strick" let programmer = new Programmer("freedom"); programmer.name; //"freedom"
2)private
當成員被修飾為private時,只能在類的內部訪問它,例如在基類Person中聲明一個私有的age屬性,在類的實例或派生類的實例中訪問age屬性都會在編譯階段報錯,如下所示。
class Person { private age: number; } person.age; //錯誤 programmer.age; //錯誤
當構造函數被修飾為private時(如下所示),包含它的類既不能實例化,也不能被繼承。
class Person { private constructor(name: string) { this.name = name; } }
3)protected
此修飾符與private的行為類似,只是有一點不同,即在派生類中還是可以訪問它的,例如在基類Person中聲明一個受保護的school屬性,在派生類中就能訪問到它,如下所示(省略了基類的構造函數)。
class Person { protected school: string; } class Programmer extends Person { constructor(name: string) { super(name); this.school = "university"; } }
當構造函數被修飾為protected時(如下所示),包含它的類不能實例化,但可以被繼承。
class Person { protected constructor(name: string) { this.name = name; } }
4)readonly
當成員被修飾為readonly時,它就變成只讀的,只能在聲明時或構造函數里初始化,其它地方對它的修改都是禁止的,如下所示。
class Person { readonly gender: string = "女"; //正確 constructor() { this.gender = "男"; //正確 } } let person = new Person(); person.gender = "女"; //錯誤
當readonly與其它修飾符一起使用時,需跟在它們后面,如下所示。
class Person { protected readonly gender: string; }
三、參數屬性
參數屬性可以便捷的在構造函數中聲明並初始化一個類的屬性,此類參數會與三個訪問修飾符或readonly組合使用,如下所示。
class Person { constructor(public name: string) { } }
構造函數中的name是一個參數屬性,相當於在Person類中聲明一個name屬性,並在構造函數中為其初始化,如下所示。
class Person { public name: string; constructor(name: string) { this.name = name; } }
四、抽象類
抽象類是供其它派生類繼承的基類,它與接口一樣,不能被實例化,但可以包含成員的實現細節。在聲明一個類時,如果包含abstract關鍵字,那么這就是一個抽象類,如下所示,當對其進行實例化時,會在編譯時報錯。
abstract class Person { } let person = new Person(); //錯誤
在抽象類中,會聲明一個或多個帶abstract類修飾符的抽象方法,它們只有名稱,不包含實現細節,可與訪問修飾符組合使用,如下所示。
abstract class Person { protected abstract work(): void }
派生類中必須實現繼承的抽象方法(如下所示),否則會在編譯階段報錯。
class Programmer extends Person { public work(): void { console.log("code"); } }