TypeScript 類(Class)
自 ES6 起,終于迎來了 class
,對于開發(fā)者來說,終于可以使用基于類的面向?qū)ο笫骄幊?。TypeScript 在原 ES6 中類的基礎(chǔ)上,還添加了一些新的功能,比如幾種訪問修飾符,這是在其他面向?qū)ο笳Z言中早就實現(xiàn)了的。
JavaScript 的類作為語法糖,我們不但需要知道怎么去使用,還應(yīng)該了解其本質(zhì),涉及到原型的部分希望大家能深入理解。
1. 慕課解釋
類描述了所創(chuàng)建的對象共同的屬性和方法。通過 class
關(guān)鍵字聲明一個類,主要包含以下模塊:
- 屬性
- 構(gòu)造函數(shù)
- 方法
2. 類的本質(zhì)
JavaScript 中,生成實例對象可以通過構(gòu)造函數(shù)的方式:
function Calculate (x, y) {
this.x = x
this.y = y
}
Calculate.prototype.add = function () {
return this.x + this.y
}
var calculate = new Calculate(1, 2)
console.log(calculate.add()) // 3
如果通過 class
關(guān)鍵字進(jìn)行改寫:
class Calculate {
// 類的屬性
public x: number
public y: number
// 構(gòu)造函數(shù)
constructor(x: number, y: number) {
this.x = x
this.y = y
}
// 類的方法
add () {
return this.x + this.y
}
}
const calculate = new Calculate(1, 2)
console.log(calculate.add()) // 3
console.log(typeof Calculate) // 'function'
console.log(Calculate === Calculate.prototype.constructor) // true
代碼解釋:
最后一行,可以看出,類指向其構(gòu)造函數(shù)本身,class
關(guān)鍵字可以看做是一個語法糖。
constructor()
方法是類的默認(rèn)方法,通過 new
來生成對象實例時,自動調(diào)用該方法。換句話說,constructor()
方法默認(rèn)返回實例對象 this
。
3. 類的繼承
基于類的程序設(shè)計中一種最基本的模式是允許使用繼承來擴(kuò)展現(xiàn)有的類,這樣可以抽出公共部分讓子類復(fù)用。
使用 extends
關(guān)鍵字來實現(xiàn)繼承:
// 繼承 JavaScript 內(nèi)置的 Date 對象
class LinDate extends Date {
getFormattedDate() {
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
return this.getDate() + "-" + months[this.getMonth()] + "-" + this.getFullYear();
}
}
const date = new LinDate()
console.log(date.getFullYear()); // 2020
console.log(date.getFormattedDate()) // 7-Jan-2020
代碼解釋: LinDate
繼承了 Date
的功能,可以使用父類 Date
的方法 getFullYear()
,也可以使用自身的方法 getFormattedDate()
。
子類在 constructor
內(nèi)中使用 super()
方法調(diào)用父類的構(gòu)造函數(shù),在一般方法內(nèi)使用 super.method()
執(zhí)行父類的方法。
class Animal {
public name:string
constructor(name: string) {
this.name = name
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`)
}
}
class Dog extends Animal {
constructor(name: string) {
// 調(diào)用父類的構(gòu)造函數(shù)
super(name)
}
move(distance = 10) {
console.log('bark...')
// 執(zhí)行父類的方法
super.move(distance)
}
}
const dog: Animal = new Dog('Coco')
dog.move() // Coco moved 10m.
代碼解釋:
第 16 行,通過 super()
調(diào)用了父類的構(gòu)造函數(shù)。
第 22 行,通過 super
關(guān)鍵字調(diào)用父類的方法。
4. 訪問修飾符
TypeScript 可以使用四種訪問修飾符 public、protected、private 和 readonly。
4.1 public
TypeScript 中,類的成員全部默認(rèn)為 public
,當(dāng)然你也可以顯式的將一個成員標(biāo)記為 public
,標(biāo)記為 public
后,在程序類的外部可以訪問。
class Calculate {
// 類的屬性
public x: number
public y: number
// 構(gòu)造函數(shù)
public constructor(x: number, y: number) {
this.x = x
this.y = y
}
public add () {
return this.x + this.y
}
}
4.2 protected
當(dāng)成員被定義為 protected
后,只能被類的內(nèi)部以及類的子類訪問。
class Base {
protected baseUrl: string = 'http://api.com/'
constructor() {}
protected request(method: string) {
const url = `${this.baseUrl}${method}`
// TODO 封裝基礎(chǔ)的 http 請求
}
}
class Address extends Base {
get() {
return this.request('address')
}
}
代碼解釋:
第 2 行,Base 類的屬性 baseUrl 被定義為受保護(hù)的,那么第 7 行該屬性在類中被訪問是可以的。
第 14 行,因 Address 類是 Base 類的子類,在子類中允許訪問父類中被定義為受保護(hù)類型的方法 request() 。
4.3 private
當(dāng)類的成員被定義為 private
后,只能被類的內(nèi)部訪問。
class Mom {
private labour() {
return 'baby is coming'
}
}
class Son extends Mom {
test () {
this.labour() // Error, Property 'labour' is private and only accessible within class 'Mom'
}
}
代碼解釋:
第 9 行,父類中的 labour() 方法被定義為私有方法,只能在父類中被使用,子類中調(diào)用報錯。
4.4 readonly
通過 readonly
關(guān)鍵字將屬性設(shè)置為只讀的。只讀屬性必須在聲明時或構(gòu)造函數(shù)里被初始化。
class Token {
readonly secret: string = 'xjx*xh3GzW#3'
readonly expired: number
constructor (expired: number) {
this.expired = expired
}
}
const token = new Token(60 * 60 * 24)
token.expired = 60 * 60 * 2 // Error, expired 是只讀的
代碼解釋:
最后一行,因 Token 類的屬性 expired 被設(shè)置為只讀屬性,不可被修改。
5. 靜態(tài)方法
通過 static
關(guān)鍵字來創(chuàng)建類的靜態(tài)成員,這些屬性存在于類本身上面而不是類的實例上。
class User {
static getInformation () {
return 'This guy is too lazy to write anything.'
}
}
User.getInformation() // OK
const user = new User()
user.getInformation() // Error 實例中無此方法
代碼解釋: getInformation() 方法被定義為靜態(tài)方法,只存在于類本身上,類的實例無法訪問。
靜態(tài)方法調(diào)用同一個類中的其他靜態(tài)方法,可使用 this 關(guān)鍵字。
class StaticMethodCall {
static staticMethod() {
return 'Static method has been called'
}
static anotherStaticMethod() {
return this.staticMethod() + ' from another static method'
}
}
代碼解釋: 靜態(tài)方法中的 this
指向類本身,而靜態(tài)方法也存在于類本身,所以可以在靜態(tài)方法中用 this 訪問在同一類中的其他靜態(tài)方法。
非靜態(tài)方法中,不能直接使用 this
關(guān)鍵字來訪問靜態(tài)方法。而要用類本身或者構(gòu)造函數(shù)的屬性來調(diào)用該方法:
class StaticMethodCall {
constructor() {
// 類本身調(diào)用
console.log(StaticMethodCall.staticMethod())
// 構(gòu)造函數(shù)的屬性調(diào)用
console.log(this.constructor.staticMethod())
}
static staticMethod() {
return 'static method has been called.'
}
}
代碼解釋: 類指向其構(gòu)造函數(shù)本身,在非靜態(tài)方法中,this.constructor === StaticMethodCall
為 true
, 也就是說這兩種寫法等價。
6. 抽象類
抽象類作為其它派生類的基類使用,它們一般不會直接被實例化,不同于接口,抽象類可以包含成員的實現(xiàn)細(xì)節(jié)。
abstract
關(guān)鍵字是用于定義抽象類和在抽象類內(nèi)部定義抽象方法。
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
const animal = new Animal() // Error, 無法創(chuàng)建抽象類實例
通常我們需要創(chuàng)建子類繼承抽象類,將抽象類中的抽象方法一一實現(xiàn),這樣在大型項目中可以很好的約束子類的實現(xiàn)。
class Dog extends Animal {
makeSound() {
console.log('bark bark bark...')
}
}
const dog = new Dog()
dog.makeSound() // bark bark bark...
dog.move() // roaming the earch...
7. 把類當(dāng)做接口使用
類也可以作為接口來使用,這在項目中是很常見的。
class Pizza {
constructor(public name: string, public toppings: string[]) {}
}
class PizzaMaker {
// 把 Pizza 類當(dāng)做接口
static create(event: Pizza) {
return new Pizza(event.name, event.toppings)
}
}
const pizza = PizzaMaker.create({
name: 'Cheese and nut pizza',
toppings: ['pasta', 'eggs', 'milk', 'cheese']
})
第 7 行,把 Pizza 類當(dāng)做接口。
因為接口和類都定義了對象的結(jié)構(gòu),在某些情況下可以互換使用。如果你需要創(chuàng)建一個可以自定義參數(shù)的實例,同時也可以進(jìn)行類型檢查,把類當(dāng)做接口使用不失為一個很好的方法。
這就是 TypeScript 的強(qiáng)大功能,而且非常靈活,擁有全面的面向?qū)ο笤O(shè)計和通用的類型檢查。
8. 小結(jié)
本節(jié)介紹了類的本質(zhì)及其使用方法,需要注意:
- 類指向其構(gòu)造函數(shù)本身。
- 靜態(tài)方法存在于類本身上面而不是類的實例上。
- 抽象類中的抽象方法不包含具體實現(xiàn)并且必須在派生類中實現(xiàn)。
- TypeScript 新增了 public、protected、private 等訪問修飾符。
- 子類繼承父類時,在其構(gòu)造函數(shù) constructor() 中不要忘了 super() 方法。