TypeScript 數(shù)據(jù)類型——枚舉 (Enum)

如 TypeScript 官方文檔所說(shuō),枚舉類型是對(duì) JavaScript 標(biāo)準(zhǔn)數(shù)據(jù)類型集的擴(kuò)充梢灭。對(duì)于熟悉 C# 的開發(fā)者來(lái)說(shuō)夷家,枚舉類型并不陌生,它能夠給一系列數(shù)值集合提供友好的名稱敏释,也就是說(shuō)枚舉表示的是一個(gè)命名元素的集合库快,因而它能夠使您的程序避免因硬編碼的值而顯雜亂且難以維護(hù)。

今天钥顽,我們將圍繞枚舉類型展開以下幾個(gè)內(nèi)容:

  • 枚舉基礎(chǔ)
  • 背后的 JavaScript 及其可擴(kuò)充性
  • 常量枚舉
  • 最佳實(shí)踐

枚舉基礎(chǔ)

默認(rèn)情況下义屏,枚舉是基于 0 的,也就是說(shuō)第一個(gè)值是 0蜂大,后面的值依次遞增闽铐。不要擔(dān)心,當(dāng)中的每一個(gè)值都可以顯式指定县爬,只要不出現(xiàn)重復(fù)即可阳啥,沒有被顯式指定的值,都會(huì)在前一個(gè)值的基礎(chǔ)上遞增财喳。

enum Color {Red, Green, Blue}
let c: Color = Color.Green;  // 1

enum Color {Red = 1, Green, Blue = 4}
let c: Color = Color.Green;  // 2

枚舉有一個(gè)很方便的特性察迟,就是您也可以向枚舉傳遞一個(gè)數(shù)值,然后獲取它對(duì)應(yīng)的名稱值耳高。舉個(gè)例子扎瓶,如果我們有一個(gè)值 2,但是不清楚在 Color 枚舉中與之對(duì)應(yīng)的名稱是什么泌枪,我們就可以通過以下的方式來(lái)進(jìn)行檢索:

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];  // 'Green'

但是像上面的這種寫法不是太好概荷,因?yàn)槿绻o定的數(shù)值沒有與之對(duì)應(yīng)的枚舉項(xiàng),那么結(jié)果就是 undefined碌燕。所以误证,如果您想要得到指定枚舉項(xiàng)的字符串名稱,可以使用類似這樣的寫法:

let colorName: string = Color[Color.Green];  // 'Green'

背后的 JavaScript

不知道大家有沒有好奇過修壕,既然 JavaScript 中沒有枚舉類型愈捅,那么 TypeScript 是怎么編譯枚舉類型的,以使其成為一個(gè)合法的 JavaScript 數(shù)據(jù)類型〈瑞現(xiàn)在蓝谨,我們就以上面提到的枚舉的特性一步步分析:

  1. 我們可以通過 . 號(hào)訪問枚舉的內(nèi)容,這與訪問對(duì)象屬性的方式一樣,那么我們是否可以將其認(rèn)為一個(gè)對(duì)象譬巫,這樣就可以得到以下的結(jié)構(gòu):
var Color = {
  Red: 0,
  Green: 1,
  Blue: 2
};
  1. 我們也可以通過具體的數(shù)值獲取其對(duì)象的名稱咖楣,這種操作和數(shù)組非常相似,傳遞索引值芦昔,提取具體索引位的值诱贿,這樣順理成章得出以下的結(jié)構(gòu):
var Color = ['Red', 'Green', 'Blue'];
  1. 現(xiàn)在將上面提到的兩個(gè)想法合并起來(lái),那么現(xiàn)在很多人可能傻眼了咕缎,#1 推導(dǎo)出來(lái)的是對(duì)象瘪松,#2 推導(dǎo)出來(lái)的是數(shù)組,這兩者怎么結(jié)合起來(lái)锨阿?不要著急宵睦,想一想,數(shù)組可是一種特殊的對(duì)象墅诡,我們將推導(dǎo)寫在下面的代碼中:
// 因?yàn)閿?shù)組也是一種對(duì)象壳嚎,那么全部都用對(duì)象表示
var Color = {};

// 換一種方式改寫 #1 中的對(duì)象
Color['Red'] = 0;
Color['Green'] = 1;
Color['Blue'] = 2;

// 換一種方式改寫 #2 中的數(shù)組
Color[0] = 'Red';
Color[1] = 'Green';
Color[2] = 'Blue';
  1. 看完上面的代碼,是否感覺豁然開朗末早,現(xiàn)在我們繼續(xù)將上面的代碼精簡(jiǎn)一下:
var Color = {};
Color[Color['Red'] = 0] = 'Red';
Color[Color['Green'] = 1] = 'Green';
Color[Color['Blue'] = 2] = 'Blue';
  1. 現(xiàn)在我們看一下烟馅,如果是 TypeScript,它會(huì)將 enum Color {Red, Green, Blue} 編譯為什么樣的 JavaScript 代碼:
var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));

枚舉的可擴(kuò)充性

注意到上面生成的代碼最后一行有沒有:(Color || (Color = {}))然磷,這就意味著郑趁,如果您代碼運(yùn)行的當(dāng)前作用域中已經(jīng)存在了 Color 變量,那么就會(huì)直接在當(dāng)前 Color 變量上面擴(kuò)展(如果我們是以模塊的方式組織代碼姿搜,就不用擔(dān)心此種情況)寡润。

基于這個(gè)特性,我們可以在多個(gè)文件中分散聲明我們的枚舉類型舅柜,比如:

enum Color {
  Red,
  Green,
  Blue
}

enum Color {
  DarkRed = 3,
  DarkGreen,
  DarkBlue
}

編譯后:

var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));
(function (Color) {
    Color[Color["DarkRed"] = 3] = "DarkRed";
    Color[Color["DarkGreen"] = 4] = "DarkGreen";
    Color[Color["DarkBlue"] = 5] = "DarkBlue";
})(Color || (Color = {}));

如果我們采用此種寫法梭纹,千萬(wàn)要注意每一部分枚舉項(xiàng)值的設(shè)置,以防止值沖突的出現(xiàn)致份。

常量枚舉

如果我們?cè)诼暶髅杜e類型的時(shí)候变抽,前面加上 const 關(guān)鍵字,那么這個(gè)枚舉就是常量枚舉氮块。但是此種枚舉類型與上面提到的不一樣绍载,不一樣在哪里呢?看下面代碼及編譯后的結(jié)果:

const enum Color {
  Red,
  Green,
  Blue
}

let c: Color = Color.Green;

編譯后:

var c = 1 /* Green */;

哇滔蝉,只剩下簡(jiǎn)單的一行代碼了击儡,Color 變量沒了!也就是锰提,編譯器針對(duì)此種枚舉類型做出了如下處理:

  • 內(nèi)聯(lián)所有的枚舉使用(也就是直接內(nèi)聯(lián)此枚舉項(xiàng)的值)曙痘。
  • 不為枚舉類型生成其對(duì)應(yīng)的 JavaScript 代碼(在這里也就是不再生成 Color 變量并為其初始化)。

使用常量枚舉立肘,還有一點(diǎn)需要注意的是边坤,我們沒法像 Color[Color.Green] 這樣獲取此枚舉項(xiàng)的字符串名稱,這也歸咎于沒有枚舉類型的 JavaScript 代碼所導(dǎo)致的谅年。

最佳實(shí)踐

1. 首個(gè)枚舉項(xiàng)值設(shè)置為 1

我們現(xiàn)在都知道茧痒,首個(gè)枚舉項(xiàng)的默認(rèn)值為 0。這點(diǎn)值得注意融蹂,在 JavaScript 中旺订,0 是一個(gè)假值,那么就會(huì)出現(xiàn)一個(gè)問題超燃,如果我想要判斷一個(gè)枚舉值是否存在怎么辦区拳?我能直接 if (c) { ... } 這樣判斷嗎?顯然是不可靠的意乓,請(qǐng)看下面代碼:

enum Color {Red, Green, Blue}

let c: Color;
if (!c) {
  console.log('Yes, I am not defined.');
}

c = Color.Red;
if (!c) {
  console.log('Oops, I have a valid value, but seems I am undefined!');
}

是不是樱调,問題顯而易見,如果我們想要對(duì)此枚舉類型變量值進(jìn)行判斷届良,我們還得這么寫:typeof(c) !== 'undefined'笆凌,非常繁瑣。而如果將首個(gè)枚舉項(xiàng)的值設(shè)置為 1士葫,我們就可以放心地直接將變量放在條件判斷語(yǔ)句中進(jìn)行判斷了乞而。

2. 盡量不要為枚舉項(xiàng)手動(dòng)設(shè)置值

好像這一點(diǎn)與上面說(shuō)的很矛盾啊,其實(shí)這一條的重點(diǎn)是慢显,不要為多個(gè)枚舉項(xiàng)設(shè)置值爪模,特別是在枚舉項(xiàng)特別多的情況下,因?yàn)檫@很容易出錯(cuò)荚藻,如下面代碼所示:

enum Color {
  Red,
  Orange = 2,
  Yellow,
  Green,
  Blue,
  Indigo = 5,
  Purple
}

console.log(Color.Blue !== Color.Indigo ? 'yes' : 'no');  // 'no'

很不幸的是呻右,您原本認(rèn)為這兩個(gè)值不一樣,可結(jié)果是一樣的鞋喇,這是由于您的疏忽而導(dǎo)致的(如果 TypeScript 能夠?yàn)榇颂峁彶榈臋C(jī)制声滥,并在編譯時(shí)報(bào)錯(cuò),我們就很大程度上避免犯這樣的錯(cuò))侦香。我們可以繼續(xù)看一下編譯后的結(jié)果:

var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Orange"] = 2] = "Orange";
    Color[Color["Yellow"] = 3] = "Yellow";
    Color[Color["Green"] = 4] = "Green";
    Color[Color["Blue"] = 5] = "Blue";
    Color[Color["Indigo"] = 5] = "Indigo";
    Color[Color["Purple"] = 6] = "Purple";
})(Color || (Color = {}));

同樣落塑,Color[5] 獲取的值也具有歧義,程序的穩(wěn)定性也因此受到影響罐韩,所以如果不是出于某種特殊目的憾赁,就不要手動(dòng)去設(shè)置值了。

3. 不要為枚舉項(xiàng)設(shè)置字符串?dāng)?shù)據(jù)類型

您可能會(huì)好奇了散吵,難道除了數(shù)字類型龙考,還能設(shè)置字符串類型蟆肆?是的,的確可以晦款。但是我為什么不推薦這么做呢炎功?我們看一下下面代碼編譯后的結(jié)果就知道了:

enum Color {
  Red,
  Green,
  Blue = 'Blue Color'
}

編譯后:

var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color["Blue"] = "Blue Color";
})(Color || (Color = {}));

不難發(fā)現(xiàn),我們沒法像 Color[1] 這樣使用 Color['Blue Color'] 了缓溅,這樣就導(dǎo)致行為結(jié)果與預(yù)期的不一致性蛇损,其次,如果像下面這樣寫坛怪,TypeScript 還會(huì)報(bào)錯(cuò)淤齐,因?yàn)橄旅娴闹禌]法根據(jù)上面的字符串值繼續(xù)遞增或推導(dǎo)了:

enum Color {
  Red,
  Green = 'Green Color',
  Blue  // error: Enum member must have initializer.
}

所以這里強(qiáng)烈不建議將枚舉項(xiàng)的值設(shè)置為字符串類型。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袜匿,一起剝皮案震驚了整個(gè)濱河市更啄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌居灯,老刑警劉巖锈死,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異穆壕,居然都是意外死亡待牵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門喇勋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缨该,“玉大人,你說(shuō)我怎么就攤上這事川背》∧茫” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵熄云,是天一觀的道長(zhǎng)膨更。 經(jīng)常有香客問我,道長(zhǎng)缴允,這世上最難降的妖魔是什么荚守? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮练般,結(jié)果婚禮上矗漾,老公的妹妹穿的比我還像新娘。我一直安慰自己薄料,他們只是感情好敞贡,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著摄职,像睡著了一般誊役。 火紅的嫁衣襯著肌膚如雪获列。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天蛔垢,我揣著相機(jī)與錄音击孩,去河邊找鬼。 笑死啦桌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的及皂。 我是一名探鬼主播樟蠕,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼梧躺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起糖权,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎性芬,沒想到半個(gè)月后居兆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡感混,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年端幼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片弧满。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡婆跑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庭呜,到底是詐尸還是另有隱情滑进,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布募谎,位于F島的核電站扶关,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏数冬。R本人自食惡果不足惜节槐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拐纱。 院中可真熱鬧疯淫,春花似錦、人聲如沸戳玫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咕宿。三九已至币绩,卻和暖如春蜡秽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缆镣。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工芽突, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人董瞻。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓寞蚌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親钠糊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挟秤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容