- 關於枚舉
- 數字枚舉
- 字符串枚舉
- 異構枚舉
- 計算的和常量成員
- 運行時的枚舉與反向映射
- 常量枚舉與外部枚舉
一、關於枚舉
枚舉:一個集的枚舉是列出某些有窮序列集的所有成員的程序,或者是一種特定類型對象的計數。
在定義中可以看到這些關鍵詞:有窮序列集、成員、類型對象、計數。
在這些關鍵字中可以了解到枚舉是一組有窮數據的集合;這些數據或者類型對象被當成這個集合的成員;計數的話我的理解有兩種:前面提到有序集那么就可以使用有序的數字對數據進行標識,而前面提到的有窮就說明這些數據量是可以被計算的。
根據這些定義的內容再來理解TypeScript枚舉類型:
TypeScript枚舉的每個成員都屬於它們所在枚舉集合的一個對象,也就是說它們的數據類型名稱就是枚舉集合的名稱,例如下面這段代碼:
1 enum en { 2 No = 0, 3 Yes = 1 4 } 5 function respond(recipient: string, message:en): void {//message參數類型為定義的枚舉類型 en,指向的是en成員No、Yes 6 if(message){ 7 console.log(recipient); 8 } 9 } 10 respond("Princess Caroline", en.Yes);//這里傳入了枚舉成員Yes
關於計數相關可以了解后面的數字枚舉,以及TypeScript的一些其他細節內容。
二、數字枚舉
TypeScript在默認情況下自動采用數字枚舉,不設置初始化的時候第一個成員的值為0,可以理解它為成員標識、計數、索引。
也可以在聲明枚舉變量時可以使用初始化器對成員值進行初始化,也就是對第一個成員設置一個整數值,可以為負數,然后后面的成員就會自動一次默認實行+1的方式賦值。
1 enum En1 {//默認情況下自動采用數字枚舉 2 a, 3 b, 4 c 5 } 6 enum En2 { 7 a = -5,//使用初始化器初始化 8 b, 9 c 10 } 11 enum En3 { 12 a = 5,//使用初始化器初始化 13 b, 14 c 15 } 16 enum En4 { 17 a, 18 b = 0,//非必要的情況下不建議這樣做 19 c 20 } 21 console.log(En1);//{0: "a", 1: "b", 2: "c", a: 0, b: 1, c: 2} 22 console.log(En2);//{a: -5, -5: "a", b: -4, -4: "b", c: -3, …} 23 console.log(En3);//{5: "a", 6: "b", 7: "c", a: 5, b: 6, c: 7} 24 console.log(En4);//{0: "b", 1: "c", a: 0, b: 0, c: 1}
這里重點來看下En4,在注釋中就表示不建議這樣做,知悉看打印出來的值你會發現找不到“{0:a}”這個原本應該存在的模式,這是因為TypeScript歸根到底還是js,而其枚舉還是依據類數組來實現的,如果在中間的成員上實現初識化的話就會出現兩種情況:初識化值小於當前成員對應的位置時就會覆蓋前面的成員的,並且后面跟着的成員會連續覆蓋,直到覆蓋所有位置差;還有一種情況就是初識化值大於當前成員的對應位置時,那么中間就會發生成員空缺的現象,比如如果對枚舉對象進行遍歷就會出現中間沒有成員的現象。
三、字符串枚舉
字符串枚舉的概念很簡單,但是有細微的 運行時的差別。 在一個字符串枚舉里,每個成員都必須用字符串字面量,或另外一個字符串枚舉成員進行初始化。
1 enum Direction { 2 Up = "UP", 3 Down = "DOWN", 4 Left = "LEFT", 5 Right = "RIGHT", 6 } 7 console.log(Direction);//{Up: "UP", Down: "DOWN", Left: "LEFT", Right: "RIGHT"}
可以使用另一個成員初識化:
1 enum Direction { 2 Up = "UP", 3 Down = Up,//使用另一個成員初識化 4 Left = "LEFT", 5 Right = "RIGHT", 6 }
四、異構枚舉
所謂異構枚舉就是數字枚舉與字符串枚舉混合在一起,這顯然不是啥好想法,特別不建議這么做。
1 enum En5 { 2 a = 0, 3 b = "YES", 4 c = 3,//如果這里不進行數字枚舉初識化會報錯,要么字符串枚舉后面就都手動賦值字符串 5 d 6 } 7 console.log(En5);//{0: "a", 3: "c", 4: "d", a: 0, b: "YES", c: 3, d: 4}
五、計算的和常量成員
5.1常量枚舉成員:
沒有初始化器,被默認賦值的第一個枚舉成員為常量,其值為0。(這種解析進一步明確TypeScript枚舉的特性)
// E.X is constant: enum E { X }
不帶初始化器,但是該成員前面一個成員是數字常量,該成員為一個常量,其值為前面一個成員的數字常量+1。這也就意味着所有被默認為數值枚舉成員都是常量。
1 //E1、E2的所有成員都是常量 2 enum E1 { X, Y, Z } 3 enum E2 { 4 A = 1, B, C 5 }
5.2常量枚舉表達式:
一個枚舉表達式字面量,這就是說字面量表達式聲明的枚舉為一個常量枚舉表達式,其不可被重寫。
1 enum En2 { 2 a = -5,//使用初始化器初始化 3 b, 4 c 5 } 6 enum En3 { //En3的字面量表達式{a,b,c}也是En2的字面量表達式,這個表達式為一個常量,所以b='b'會報錯 7 a = -5, 8 b = 'b',//這里修改了枚舉表達式字面量內部一個成員的值,這是不允許的 9 c 10 } 11 enum En4 { //這個不會報錯 12 a = -5, 13 b, 14 c 15 }
一個對之前定義的常量枚舉成員的引用為一個常量枚舉表達式,這就意味着不能使用枚舉成員引用修改引用成員。
1 enum En2 { 2 a = -5,//使用初始化器初始化 3 b, 4 c 5 } 6 console.log(En2[-5]);//枚舉成員的引用獲取對應枚舉成員 7 //En2[-5]; = 'c';//這是不允許的,可以獲取枚舉成員的值,但不能重寫引用指向,因為它是一個常量
一元運算符和二元運算符應用在枚舉表達式中也都是常量枚舉表達式,(+
, -
, ~
)(+
, -
, *
, /
, %
, <<
, >>
, >>>
, &
, |
, ^
), 若常數枚舉表達式求值后為 NaN
或 Infinity
,則會在編譯階段報錯。
1 enum FileAccess { 2 // constant members常量成員 3 None, 4 Read = 1 << 1, 5 Write = 1 << 2, 6 ReadWrite = Read | Write, 7 // computed member計算成員 8 G = "123".length 9 }
六、運行時的枚舉與反向映射
在前面就有提到TypeScript的枚舉是基於類數組來實現的,當然這部絕對,比如字符串枚舉就是一個純粹的對象。如果像是數字枚舉的話,就可以實現反向映射。
1 enum Enum { 2 A 3 } 4 let a = Enum.A; 5 let nameOfA = Enum[a]; // "A"
在前面示例中打印的值來看或許你已經猜到了什么,這里來看看運行時的枚舉代碼到底是什么?
1 var Enum; 2 (function (Enum) { 3 Enum[Enum["A"] = 0] = "A"; 4 })(Enum || (Enum = {})); 5 var a = Enum.A; 6 var nameOfA = Enum[a]; // "A"
首先,它本身是一個對象,這里則是一個類數組,它包含了正向映射( name
-> value
)和反向映射( value
-> name
)。 引用枚舉成員總會生成為對屬性訪問並且永遠也不會內聯代碼。要注意的是 不會為字符串枚舉成員生成反向映射。
七、常量枚舉與外部枚舉
常量枚舉簡單的說就是給引用常量的部分直接給定一個確定的值,而不是轉碼的后的對象屬性引用表達式。來看下面這兩段代碼以及編譯代碼:
1 //常量枚舉 2 const enum Enum { 3 A = 1, 4 B = A * 2 5 } 6 console.log(Enum.A); 7 //編譯后的代碼 8 console.log(1 /* A */);
在TypeScript中常量枚舉如果沒有被使用的話不會被編譯,而使用部分只會直接使用確定的值。
1 //常量枚舉 2 const enum Directions { 3 Up, 4 Down, 5 Left, 6 Right 7 } 8 let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right] 9 //編譯后的代碼 10 var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
外部枚舉:
瞅了半天官方文檔還是沒瞅明白,哪位大佬弄明白的請留個言,感激不盡!