前言
什麼是列舉,顧名思義我們看到列舉這個詞,腦子裡就已經想到這是一個鍵值對的形式,就可以看做是我們JavaScript
中的json
物件一樣。在ts中宣告列舉物件的關鍵字是使用enum
,列舉成員一但定義就不可改變了。下面來介紹一下ts中的列舉物件。
數字列舉
我們先來說一下數字列舉
, 一目瞭然,一看就知道數字列舉的成員都是number
型別的。下面我們來看看建立一個簡單的列舉物件吧。
enum device {
phone,
notebook,
desktop
}
console.log(device.phone) // 0
上面的值是phone
0、notebook
1、desktop
2。
只要定義了列舉物件,預設不賦值,那麼當前的列舉物件裡面第一個的值從0開始依次遞增。
看下面栗子
enum device {
phone = 3,
notebook,
desktop
}
上面我們預設給phone
賦值一個3
,那麼下面兩個值也會預設遞增,會變成notebook = 4
、desktop = 5
。
注意事項 - 列舉物件遞增
- 列舉物件成員遞增值為1
- 列舉物件成員遞增只會看當前值的前一個列舉成員是否有值,有值的話後面依次跟著遞增。跟第一個列舉成員值無關
數字列舉物件會存在反向對映
enum device {
phone
notebook,
desktop
}
通過上面栗子我們可以知道,device.notebook = 1
。但還可以通過device[1]
獲取出來notebook
,這就是因為存在反向對映
(key和value可以互相訪問)。
需要注意的是,只有數字列舉成員才會有反向對映,字串或其它值是沒有的。
來看下列舉物件被編譯後的
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fd33df8cc99f4517907ffa807af4f973~tplv-k3u1fbpfcp-watermark.image" width="100%"/>
我們再來看一個問題
enum device {
phone = 2,
notebook = 1,
desktop
}
console.log(device.phone) // 2
console.log(device[2]) // desktop
可以看到上面程式碼中,device.phone = 2
,然後我們使用反向這種方式訪問device[2]
卻是desktop
。這是因為預設我們給phone
賦值為2
,然後又給notebook
賦值為1
,這時desktop
跟著上一個列舉成員(notebook)值去遞增,所以這時2
已經被替換為desktop
了。
這裡需要注意,避免踩坑,ts是不會檢查出來的重複的值。
字串列舉
看過了上面的數字列舉,再來看字串列舉,顯然就明白字串列舉成員的值肯定都是string
型別。字串列舉物件是沒有反向對映的。
enum device {
phone = "1",
notebook = "2",
desktop = "3"
}
我們來看一下編譯後的程式碼
<img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bc0e79c440bf4efc99c404019ea3abed~tplv-k3u1fbpfcp-watermark.image" width="100%"/>
可以看到上面編譯後的程式碼沒有反向對映的程式碼。
字串列舉是沒有遞增的,當前的列舉成員前一個值為字串,那麼當前的列舉物件如果不賦值就會報錯。
enum device {
phone = "1",
notebook
}
上面這種情況就會導致編譯階段報錯。"列舉成員必須具有初始化表示式",待會我們就會講到列舉物件的表示式
。
異構列舉
異構列舉是啥呢,直白的說就是一個列舉物件中可以包括數字列舉成員
和字串列舉成員
,就是可以混合使用。但是我們要正兒八經的使用列舉物件的話,一般還真不會使用異構列舉,就像文件說的一樣,似乎你並不會這麼做。
enum Person {
name = "前端娛樂圈",
age = 18
}
在看一個栗子
enum Person {
name = "前端娛樂圈",
age = 3 * 6
}
上面這種是會報錯的,“含字串值成員的列舉中不允許使用計算值”,列舉物件成員有字串的則不能再設定其它列舉物件成員為計算的值(3 * 6)
。但是可以直接寫字面量的。下面我們會講到計算
計算的和常量成員
列舉物件中的列舉成員都帶有一個值,這個值是計算的
或常量
。那麼怎麼看是計算的還是常量呢。這就講到我們上面說的列舉物件成員表示式
,只要是表示式那一定就是常量
否則就是計算的
。所以只需要知道列舉成員是表示式就知道它就是常量。
當滿足下面其中一個條件那麼它就是一個表示式。以下借用官方的規則條件
- 一個列舉表示式字面量(主要是字串字面量或數字字面量)
- 一個對之前定義的常量列舉成員的引用(可以是在不同的列舉型別中定義的)
- 帶括號的常量列舉表示式
- 一元運算子
+
,-
,~
其中之一應用在了常量列舉表示式 - 常量列舉表示式做為二元運算子
+
,-
,*
,/
,%
,<<
,>>
,>>>
,&
,|
,^
的操作物件。 若常數列舉表示式求值後為NaN
或Infinity
,則會在編譯階段報錯。
上面這些條件成立之後那麼當前列舉成員就是一個常量。常量就是可以在編譯階段求值的
常量
enum obj {
index, // 滿足條件 常量
index1 = index, // 滿足條件 常量
age = 2 << 1, // 滿足條件 常量
num = 30 | 2, // 滿足條件 常量
num1 = 10 + 29 // 滿足條件 常量
}
在看一下上面編譯後的程式碼,可以看到直接在編譯階段求值了。
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/42beb700d9cd4d0a81d756d12384910b~tplv-k3u1fbpfcp-watermark.image" width="100%"/>
計算的
enum obj {
nameLen = "前端娛樂圈".length, // 計算的
num = Math.random() * 100 // 計算的
}
在看一下計算的編譯後的程式碼是沒有求值的。
<img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b517b83eba894ce88c1973b77478f37f~tplv-k3u1fbpfcp-watermark.image" width="100%"/>
const列舉
一般情況下,普通列舉物件就可以滿足我的需求,但是有些情況比如為了節省額外的開銷和效能,我們可以選擇使用常量列舉,常量列舉使用const
關鍵字定義,它與普通列舉不同的時,它會在編譯階段刪除該物件,且不能訪問該列舉物件,只能訪問該列舉物件成員。常量列舉的成員只能是常量列舉表示式,不可以使用計算值
const enum obj {
A = 1,
B = 3 * 6,
C = 1 & 2
}
console.log(obj) // 報錯
需要注意上面這個常量列舉物件編譯後的物件也是"空"的,系統自動刪除
const enum obj {
A = 1,
B = 3 * 6,
C = 1 & 2
}
console.log(obj.A) // 1
console.log(obj.B) // 8
console.log(obj.C) // 0
那麼上面這個栗子,編譯後只能看見console.log
這些值。
<img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c34317c5a0c54d8a8ac34f9823c16a21~tplv-k3u1fbpfcp-watermark.image" width="100%"/>
外部列舉
外部列舉使用declare
關鍵字定義,文件描述:外部列舉用來描述已經存在的列舉型別的形狀,意思就是說外部列舉用來描述當前環境中存在的列舉物件。外部列舉和普通列舉的一個區別就是,在外部列舉裡面沒有初始化的列舉成員會當成一個計算值
,而在普通列舉裡面則是一個常量
。
declare enum Enum {
A = 1,
B,
C = 2
}
console.log(Enum);
console.log(Enum.A)
上面這種執行完,你會發現不管執行列舉本身還是列舉成員都是報錯,"Enum is not defined"。因為外部列舉編譯後壓根就沒有生成。所以有明白外部列舉用途的小夥伴給解答一下,謝謝啦~
列舉成員的型別
列舉成員也可以被當做一個型別,就是可以指定某些變數的值必須是列舉成員的值。
列舉成員成為型別必須滿足其中之一條件
- 字面量列舉成員是指不帶有初始值的常量列舉成員
- 任何字串字面量(如:
A
、B
) - 任何數字字面量(如:
1
,100
) - 應用了一元
-
符號的數字字面量(如:-1
,-100
)
enum obj {
name = "前端娛樂圈",
num = -100
}
interface msg {
title: obj.name;
num: obj.num
}
let json: msg = {
title: obj.name,
num: obj.num
}
感謝
謝謝閱讀,如有幫助請點個關注、收藏吧
覺得有幫助可以關注 前端娛樂圈 公眾號,每天為你推送一篇小知識
大家也可以加我 微信 交個朋友,可以找我聊天或拉你進技術交流群