
這段時(shí)間有點(diǎn)忙,今天開始又空閑了,那就繼續(xù)我們的TypeScript之旅!
總覽:TypeScript圖形渲染實(shí)戰(zhàn)(2D架構(gòu)設(shè)計(jì)和實(shí)現(xiàn))詳介
TypeScript圖形渲染實(shí)戰(zhàn)2D架構(gòu)設(shè)計(jì)與實(shí)現(xiàn):第2章 使用TypeScript實(shí)現(xiàn)Doom3詞法解析器(1)
TypeScript圖形渲染實(shí)戰(zhàn)2D架構(gòu)設(shè)計(jì)與實(shí)現(xiàn):第2章 使用TypeScript實(shí)現(xiàn)Doom3詞法解析器(2:Token與Tokenizer)
在上一節(jié)中,我們主要介紹了在TypeScript中如何:
- import引入模塊中定義的類、函數(shù)、變量以及類型等,export導(dǎo)出模塊中的自定義的接口;
- 聲明接口方法;
- 聲明接口的(只讀)屬性;
- ES6中的模版字符串;
今天的文章中,我們將會(huì)通過實(shí)現(xiàn)IDoom3Token接口來了解TypeScript如下幾個(gè)要點(diǎn):
- 使用implements關(guān)鍵字實(shí)現(xiàn)接口;
- 使用class關(guān)鍵字聲明類;
- 類的三個(gè)訪問級(jí)別:private / protected / public,默認(rèn)為public;
- TypeScript中類型數(shù)組的初始化;
- 類的成員變量的初始化;
- 成員變量的顯示斷言賦值聲明;
- constructor關(guān)鍵字實(shí)現(xiàn)構(gòu)造函數(shù)
正文:
2.2 IDoom3Token與IDoom3Tokenizer接口的實(shí)現(xiàn)
??在上一節(jié)中我們聲明了IDoom3Token接口和IDoom3Tokenizer接口,本節(jié)我們來看一下這兩個(gè)接口的具體實(shí)現(xiàn)過程。我們會(huì)發(fā)現(xiàn)接口和實(shí)現(xiàn)類之間的微妙關(guān)系,既:接口規(guī)定了要做什么,接口的實(shí)現(xiàn)類則規(guī)定了應(yīng)該怎么去做。
2.2.1 Doom3Token類成員變量的聲明
??首先我們來看一下IDoom3Token接口的實(shí)現(xiàn)類,在TypeScript中使用implements關(guān)鍵字來實(shí)現(xiàn)一個(gè)接口,代碼如下所示:
class Doom3Token implements IDoom3Token {
private _type : ETokenType ; // 標(biāo)識(shí)當(dāng)前token的類型 : NONE / STRING / NUMBER
private _charArr : string [ ] = [ ] ; // 字符串?dāng)?shù)組
private _val : number ; // 如果當(dāng)前的token類型是NUMBER,則會(huì)設(shè)置該數(shù)值,如果是字符串類型,就忽略該變量
}
??上面代碼很簡(jiǎn)單,但是也有幾個(gè)值得我們關(guān)注的地方:
- 在TypeScript / JavaScript中,并沒有char這個(gè)數(shù)據(jù)類型,都是使用string類型來表示單個(gè)字符,我們?cè)谧兞縚charArr中存放的實(shí)際是char(一個(gè)字符)類型的數(shù)據(jù)。
- 在TypeScript中,有兩種聲明和實(shí)列化(內(nèi)存分配)類型數(shù)組的方式,第一種就是我們上面所使用的方式,另外一種可以使用_charArray : Array < number > = new Array < string > ( )的方式,筆者更喜歡第一種方式來聲明類型數(shù)組變量,簡(jiǎn)潔明了少寫幾個(gè)字。
- 我們會(huì)看到在聲明IDoom3Token接口時(shí)使用了export關(guān)鍵字來導(dǎo)出接口,但是在實(shí)現(xiàn)類Doom3Token中并沒有使用export關(guān)鍵字。這是接口的一個(gè)很棒的特性:我們只想暴露(export)接口(interface),我們想隱藏類(class)的實(shí)現(xiàn),第三方調(diào)用時(shí),只關(guān)心接口是怎么使用的,不需要知道具體類是怎么實(shí)現(xiàn)的。
- TypeScript支持public / protected / private 三個(gè)級(jí)別的訪問修飾符,如果你沒有在成員變量前聲明訪問修飾符,在默認(rèn)情況下,被定義為public級(jí)別。關(guān)于三個(gè)訪問修飾符的區(qū)別如下:
- 被public訪問修飾符修飾的成員變量或方法能夠被所有類訪問。
- 被protected訪問修飾符修飾的成員變量或方法既能被定義它的類也能被繼承它的子類訪問。
- 被private訪問修飾符修飾的成員變量或方法只能被定義它的類訪問,也就是說不能在聲明它的類的外部訪問。
2.2.2 Doom3Token類變量初始化的問題
??接下來繼續(xù)來看一下構(gòu)造函數(shù)(constructor關(guān)鍵字)和reset函數(shù),代碼如下:
public constructor ( ) {
this . _charArr . length = 0 ;
this . _type = ETokenType . NONE ;
this . _val = 0.0 ;
}
public reset ( ) : void {
this . _charArr . length = 0 ;
this . _type = ETokenType . NONE ;
this . _val = 0.0 ;
}
??我們會(huì)發(fā)現(xiàn)constructor中的代碼和reset中的代碼一模一樣,那么讀者可能會(huì)問,為什么不在constructor中直接調(diào)用reset函數(shù)呢?
??其實(shí)這里涉及到TypeScript對(duì)成員變量初始化的時(shí)機(jī)點(diǎn)問題。大家可以試一下,如果我們?cè)赾onstructor中調(diào)用reset函數(shù),TypeScript編譯器會(huì)報(bào)“xxx屬性沒有初始化表達(dá)式,且未在構(gòu)造函數(shù)中明確賦值。”的錯(cuò)誤,如下圖2.2所示。

??從上述錯(cuò)誤描述中我們可以知道,TypeScript對(duì)于成員變量的初始化有兩個(gè)時(shí)機(jī)點(diǎn),第一個(gè)時(shí)機(jī)點(diǎn)是在成員變量聲明時(shí)立即進(jìn)行賦值(初始化),如private _charArr : string [ ] = [ ] ; 這句代碼所示,這種稱為初始化表達(dá)式。
??如果不在成員變量聲明時(shí)立即賦值的話,那么就只能是在constructor構(gòu)造函數(shù)中進(jìn)行變量賦值(初始化)。但是你會(huì)發(fā)現(xiàn),有時(shí)候延遲初始化或重新初始化是很有必要的一種操作。幸運(yùn)的是,從TypeScript 2.7版本開始支持使用!(感嘆號(hào))來進(jìn)行變量的顯示斷言賦值聲明,我們來修改一下代碼,看一下效果,具體代碼如下:
// 使用!操作符來進(jìn)行顯示斷言賦值聲明
private _val ! : number ;
private _type ! : ETokenType ;
public constructor ( ) {
// this . _charArr . length = 0 ;
// this . _type = ETokenType . NONE ;
// this . _val = 0.0 ;
this . reset ( ) ;
}
??我們會(huì)發(fā)現(xiàn),TypeScript不再報(bào)初始化的錯(cuò)誤了,是不是很棒的感覺。這是一個(gè)很有用的功能,可以讓我們靈活的處理變量初始化的問題,因此值得在這里花點(diǎn)時(shí)間討論一下。還是需要強(qiáng)調(diào)一點(diǎn),在使用該變量前一定要初始化變量。
2.2.3 IDoom3Token接口方法的實(shí)現(xiàn)
??接下來我們看一下Doom3Token類的其他幾個(gè)接口方法的實(shí)現(xiàn),具體代碼如下:
// 使用get關(guān)鍵字來定義屬性,get定義只讀屬性,set定義只寫屬性
public get type ( ) : ETokenType {
return this . _type ;
}
//獲取當(dāng)前token的字符串值
public getString ( ) : string {
// _charArr數(shù)組中存放的都是單個(gè)字符序列,例如[ d , o , o , m , 3 ]
// 我們可以使用數(shù)組的join方法將字符串聯(lián)成字符串
// 下面會(huì)使用join方法后,會(huì)返回doom3這個(gè)字符串
return this . _charArr . join ( "" ) ;
}
// 獲取當(dāng)前token的浮點(diǎn)值
public getFloat ( ) : number {
return this . _val ;
}
// 獲取當(dāng)前token的int類型值
public getInt ( ) : number {
// 使用parserInt函數(shù)
// 第一個(gè)參數(shù)是一個(gè)字符串類型的數(shù)字表示
// 第二個(gè)參數(shù)是進(jìn)制,我們一般用10進(jìn)制
return parseInt ( this . _val . toString ( ) , 10 ) ;
}
??我們來看一個(gè)字符串比較的接口方法的實(shí)現(xiàn),具體代碼如下所示:
public isString ( str : string ) : boolean {
let count : number = this . _charArr . length ;
// 字符串長(zhǎng)度不相等,肯定不等
if ( str . length !== count ) {
return false ;
}
// 遍歷每個(gè)字符
for ( let i : number = 0 ; i < count ; i++ ) {
// _charArr數(shù)組類型中每個(gè)char和輸入的string類型中的每個(gè)char進(jìn)行嚴(yán)格比較(!==操作符而不是!=)
// 只要任意一個(gè)char不相等,意味著整個(gè)字符串都不相等
if ( this . _charArr [ i ] !== str [ i ] ) {
return false ;
}
}
// 完全相等
return true ;
}
2.2.4 Doom3Token類的非接口方法實(shí)現(xiàn)
??至此我們介紹了所有的接口方法的實(shí)現(xiàn)以及涉及到的、與TypeScript相關(guān)的語言要點(diǎn),這些接口方法都是被第三方調(diào)用的,我們還要增加一些方法,這些方法由實(shí)現(xiàn)的內(nèi)部類(例如IDoom3Tokenizer的實(shí)現(xiàn)類Doom3Tokenizer)所調(diào)用,但是它們并不需要被公開給第三方使用,下面我們就關(guān)注這些方法,具體代碼如下所示:
// 下面三個(gè)非接口方法被IDoom3Tokenizer接口的實(shí)現(xiàn)類Doom3Tokenizer所使用
// 將一個(gè)char添加到_charArr數(shù)組的尾部
public addChar ( c : string ) : void {
this . _charArr . push ( c ) ;
}
// 設(shè)置數(shù)字,并將類型設(shè)置為NUMBER
public setVal ( num : number ) : void {
this . _val = num ;
this . _type = ETokenType . NUMBER ;
}
//設(shè)置類型
public setType ( type : ETokenType ) : void {
this . _type = type ;
}