TypeScript入门七:TypeScript的枚举
- 关于枚举
- 数字枚举
- 字符串枚举
- 异构枚举
- 计算的和常量成员
- 运行时的枚举与反向映射
- 常量枚举与外部枚举
一、关于枚举
枚举:一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。
在定义中可以看到这些关键词:有穷序列集、成员、类型对象、计数。
在这些关键字中可以了解到枚举是一组有穷数据的集合;这些数据或者类型对象被当成这个集合的成员;计数的话我的理解有两种:前面提到有序集那么就可以使用有序的数字对数据进行标识,而前面提到的有穷就说明这些数据量是可以被计算的。
根据这些定义的内容再来理解TypeScript枚举类型:
TypeScript枚举的每个成员都属于它们所在枚举集合的一个对象,也就是说它们的数据类型名称就是枚举集合的名称,例如下面这段代码:
enum en { No = 0, Yes = 1 } function respond(recipient: string, message:en): void {//message参数类型为定义的枚举类型 en,指向的是en成员No、Yes if(message){ console.log(recipient); } } respond("Princess Caroline", en.Yes);//这里传入了枚举成员Yes
关于计数相关可以了解后面的数字枚举,以及TypeScript的一些其他细节内容。
二、数字枚举
TypeScript在默认情况下自动采用数字枚举,不设置初始化的时候第一个成员的值为0,可以理解它为成员标识、计数、索引。
也可以在声明枚举变量时可以使用初始化器对成员值进行初始化,也就是对第一个成员设置一个整数值,可以为负数,然后后面的成员就会自动一次默认实行+1的方式赋值。
enum En1 {//默认情况下自动采用数字枚举 a, b, c } enum En2 { a = -5,//使用初始化器初始化 b, c } enum En3 { a = 5,//使用初始化器初始化 b, c } enum En4 { a, b = 0,//非必要的情况下不建议这样做 c } console.log(En1);//{0: "a", 1: "b", 2: "c", a: 0, b: 1, c: 2} console.log(En2);//{a: -5, -5: "a", b: -4, -4: "b", c: -3, …} console.log(En3);//{5: "a", 6: "b", 7: "c", a: 5, b: 6, c: 7} console.log(En4);//{0: "b", 1: "c", a: 0, b: 0, c: 1}
这里重点来看下En4,在注释中就表示不建议这样做,知悉看打印出来的值你会发现找不到“{0:a}”这个原本应该存在的模式,这是因为TypeScript归根到底还是js,而其枚举还是依据类数组来实现的,如果在中间的成员上实现初识化的话就会出现两种情况:初识化值小于当前成员对应的位置时就会覆盖前面的成员的,并且后面跟着的成员会连续覆盖,直到覆盖所有位置差;还有一种情况就是初识化值大于当前成员的对应位置时,那么中间就会发生成员空缺的现象,比如如果对枚举对象进行遍历就会出现中间没有成员的现象。
三、字符串枚举
字符串枚举的概念很简单,但是有细微的 运行时的差别。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT", } console.log(Direction);//{Up: "UP", Down: "DOWN", Left: "LEFT", Right: "RIGHT"}
可以使用另一个成员初识化:
enum Direction { Up = "UP", Down = Up,//使用另一个成员初识化 Left = "LEFT", Right = "RIGHT", }
四、异构枚举
所谓异构枚举就是数字枚举与字符串枚举混合在一起,这显然不是啥好想法,特别不建议这么做。
enum En5 { a = 0, b = "YES", c = 3,//如果这里不进行数字枚举初识化会报错,要么字符串枚举后面就都手动赋值字符串 d } 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。这也就意味着所有被默认为数值枚举成员都是常量。
//E1、E2的所有成员都是常量 enum E1 { X, Y, Z } enum E2 { A = 1, B, C }
5.2常量枚举表达式:
一个枚举表达式字面量,这就是说字面量表达式声明的枚举为一个常量枚举表达式,其不可被重写。
enum En2 { a = -5,//使用初始化器初始化 b, c } enum En3 { //En3的字面量表达式{a,b,c}也是En2的字面量表达式,这个表达式为一个常量,所以b=‘b‘会报错 a = -5, b = ‘b‘,//这里修改了枚举表达式字面量内部一个成员的值,这是不允许的 c } enum En4 { //这个不会报错 a = -5, b, c }
一个对之前定义的常量枚举成员的引用为一个常量枚举表达式,这就意味着不能使用枚举成员引用修改引用成员。
enum En2 { a = -5,//使用初始化器初始化 b, c } console.log(En2[-5]);//枚举成员的引用获取对应枚举成员 //En2[-5]; = ‘c‘;//这是不允许的,可以获取枚举成员的值,但不能重写引用指向,因为它是一个常量
一元运算符和二元运算符应用在枚举表达式中也都是常量枚举表达式,(+
, -
, ~
)(+
, -
, *
, /
, %
, <<
, >>
, >>>
, &
, |
, ^
), 若常数枚举表达式求值后为 NaN
或 Infinity
,则会在编译阶段报错。
enum FileAccess { // constant members常量成员 None, Read = 1 << 1, Write = 1 << 2, ReadWrite = Read | Write, // computed member计算成员 G = "123".length }
六、运行时的枚举与反向映射
在前面就有提到TypeScript的枚举是基于类数组来实现的,当然这部绝对,比如字符串枚举就是一个纯粹的对象。如果像是数字枚举的话,就可以实现反向映射。
enum Enum { A } let a = Enum.A; let nameOfA = Enum[a]; // "A"
在前面示例中打印的值来看或许你已经猜到了什么,这里来看看运行时的枚举代码到底是什么?
var Enum; (function (Enum) { Enum[Enum["A"] = 0] = "A"; })(Enum || (Enum = {})); var a = Enum.A; var nameOfA = Enum[a]; // "A"
首先,它本身是一个对象,这里则是一个类数组,它包含了正向映射( name
-> value
)和反向映射( value
-> name
)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。要注意的是 不会为字符串枚举成员生成反向映射。
七、常量枚举与外部枚举
常量枚举简单的说就是给引用常量的部分直接给定一个确定的值,而不是转码的后的对象属性引用表达式。来看下面这两段代码以及编译代码:
//常量枚举 const enum Enum { A = 1, B = A * 2 } console.log(Enum.A); //编译后的代码 console.log(1 /* A */);
在TypeScript中常量枚举如果没有被使用的话不会被编译,而使用部分只会直接使用确定的值。
//常量枚举 const enum Directions { Up, Down, Left, Right } let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right] //编译后的代码 var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
外部枚举:
瞅了半天官方文档还是没瞅明白,哪位大佬弄明白的请留个言,感激不尽!