Typescript 实战 --- (2)枚举
枚举(Enum)是一个命名元素的集合,用于取值被限定在一定范围内的场景。
作用:将程序中不容易记忆的硬编码,或者是在未来会发生改变的常量抽取出来,定义成枚举类型,以此来提高程序的可读性和可维护性
语法:
enum 枚举类名 {
枚举成员1, 枚举成员2, 枚举成员3... ... 枚举成员n;
}
1、数字枚举
使用枚举可以定义一些有名字的数字常量,枚举类型会被编译成一个双向映射的对象。枚举成员会被赋值为从0开始递增的数字,同时,也会被枚举值到枚举名进行反向映射
enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat } console.log('Days', Days); // { // '0': 'Sun', // '1': 'Mon', // '2': 'Tue', // '3': 'Wed', // '4': 'Thu', // '5': 'Fri', // '6': 'Sat', // Sun: 0, // Mon: 1, // Tue: 2, // Wed: 3, // Thu: 4, // Fri: 5, // Sat: 6 // } console.log('Days[0]:', Days[0]); // Days[0]: Sun console.log('Days.Sun:', Days.Sun); // Days.Sun: 0 console.log('Days[Days.Sun]:', Days[Days.Sun]) // Days[Days.Sun]: Sun
上例中,枚举类Days被编译成了一个js对象,该对象首先以枚举名为key,以数字常量为value,逐对新增;然后以数字常量为key,枚举值为value,逐对新增。由此,建立了一个枚举值和数字常量双向映射的对象
var Days; (function (Days) { Days[Days["Sun"] = 0] = "Sun"; Days[Days["Mon"] = 1] = "Mon"; Days[Days["Tue"] = 2] = "Tue"; Days[Days["Wed"] = 3] = "Wed"; Days[Days["Thu"] = 4] = "Thu"; Days[Days["Fri"] = 5] = "Fri"; Days[Days["Sat"] = 6] = "Sat"; })(Days || (Days = {}));
默认情况下,第一个枚举成员的值是0,后面的枚举成员的值依次加1。也可以
enum Color1 { Red, Green, Blue }; enum Color2 { Red = 2, Green, Blue }; enum Color3 { Red, Green = 3, Blue }; enum Color4 { Red, Green, Blue = 0 }; // 不建议 enum Color5 { Red = -5, Green, Blue = 0 }; // 不建议 enum Color6 { Red = -6, Green, Blue }; console.log('Color1: ', Color1); console.log('Color2: ', Color2); console.log('Color3: ', Color3); console.log('Color4: ', Color4); console.log('Color5: ', Color5); console.log('Color6: ', Color6); // Color1: { '0': 'Red', '1': 'Green', '2': 'Blue', Red: 0, Green: 1, Blue: 2 } // Color2: { '2': 'Red', '3': 'Green', '4': 'Blue', Red: 2, Green: 3, Blue: 4 } // Color3: { '0': 'Red', '3': 'Green', '4': 'Blue', Red: 0, Green: 3, Blue: 4 } // Color4: { '0': 'Blue', '1': 'Green', Red: 0, Green: 1, Blue: 0 } // Color5: { '0': 'Blue', Red: -5, '-5': 'Red', Green: -4, '-4': 'Green', Blue: 0 } // Color6: { Red: -6, '-6': 'Red', Green: -5, '-5': 'Green', Blue: -4, '-4': 'Blue' }
2、字符串枚举
字符串枚举是不可以做双向映射的,只有枚举成员的名称作为key
enum OrderStatus { Created = '已创建', Cancelled = '已取消' } // 编译 var OrderStatus; (function (OrderStatus) { OrderStatus["Created"] = "\u5DF2\u521B\u5EFA"; OrderStatus["Cancelled"] = "\u5DF2\u53D6\u6D88"; })(OrderStatus || (OrderStatus = {})); console.log(OrderStatus); // { Created: '已创建', Cancelled: '已取消' }
3、异构枚举
把数字枚举和字符串枚举混用,就形成了异构枚举,这种方式很容易引起混淆,不推荐使用
enum Result { T, F = "不通过" } // 编译 var Result; (function (Result) { Result[Result["T"] = 0] = "T"; Result["F"] = "\u4E0D\u901A\u8FC7"; })(Result || (Result = {})); console.log(Result); { '0': 'T', T: 0, F: '不通过' }
4、枚举成员的特性
4-1、枚举成员的值不可修改
Char.a = 6; // Cannot assign to 'a' because it is a read-only property
4-2、枚举成员的分类
(1)、常量枚举:没有设置初始值,对已有枚举成员的引用,常量的表达式
常量枚举成员会在编译时计算出结果,然后以常量的形式,出现在运行时环境
(2)、需要计算的非常量表达式
这些枚举成员的值不会在编译阶段计算,而是保留到程序的执行阶段
enum Char { // 常量枚举成员 a, b = Char.a, c = 1 + 3, // 非常量枚举成员 d = Math.random(), e = 'hello'.length } console.log(Char); // 编译 var Char; (function (Char) { // 常量枚举成员 Char[Char["a"] = 0] = "a"; Char[Char["b"] = 0] = "b"; Char[Char["c"] = 4] = "c"; // 非常量枚举成员 Char[Char["d"] = Math.random()] = "d"; Char[Char["e"] = 'hello'.length] = "e"; })(Char || (Char = {})); // 运行 { '0': 'b', '4': 'c', '5': 'e', a: 0, b: 0, c: 4, d: 0.5257674738591782, '0.5257674738591782': 'd', e: 5 }
(3)、出现在非常量表达式后面的枚举成员必须要赋初始值,否则会报错(如下例中的 成员f )
enum Char { // 常量枚举成员 a, b = Char.a, c = 1 + 3, // 非常量枚举成员 d = Math.random(), e = 'hello'.length, f // 枚举成员必须具有初始化表达式 }
5、常量枚举
常量枚举是在 enum关键字前使用 const 修饰符
特点:在编译阶段被移除
作用:当我们不需要一个对象,而需要对象的值,就可以使用常量枚举,这样就可以避免在编译时生成多余的代码和间接引用
const enum Month { Jan, Feb, Mar } // 编译时,没有任何输出 console.log(Month); // "const" 枚举仅可在属性、索引访问表达式、导入声明的右侧、导出分配或类型查询中使用。
常量枚举成员在使用的地方被内联进来,且常量枚举不可能有计算成员
const enum Directions { Up, Right, Down, Left } let directions = [Directions.Up, Directions.Right, Directions.Down, Directions.Left]; // 编译 var directions = [0 /* Up */, 1 /* Right */, 2 /* Down */, 3 /* Left */]; console.log(directions); [ 0, 1, 2, 3 ]
const enum Directions { Up, Right, Down, Left, UpRight = 3 + 1, RightDown = Directions.Right + 2, LeftDown = 9, // RightUp = 'hello'.length // const enum member initializers can only contain literal values and other computed enum values } let directions = [ Directions.Up, Directions.Right, Directions.Down, Directions.Left, Directions.UpRight, Directions.RightDown, Directions.LeftDown ]; // 编译 var directions = [ 0 /* Up */, 1 /* Right */, 2 /* Down */, 3 /* Left */, 4 /* UpRight */, 3 /* RightDown */, 9 /* LeftDown */ ]; console.log(directions); [ 0, 1, 2, 3, 4, 3, 9 ]
6、在某些情况下,枚举和枚举成员都可以作为单独的类型存在
enum E { a, b } enum F { a = 0, b = 1 } enum G { a = 'apple', b = 'banana' } let e: E = 4; let f: F = 4; // console.log(e === f); // This condition will always return 'false' since the types 'E' and 'F' have no overlap. let e1: E.a = 3 let e2: E.b = 3 let e3: E.a = 3 // console.log(e1 === e2); // This condition will always return 'false' since the types 'E.a' and 'E.b' have no overlap console.log(e1 === e3); // true let g1: G = G.a let g2: G.a = G.a console.log(g1 === g2); // true