前言
通俗来说,枚举就是对一个对象的所有可能取到的值的集合, 在Typescript使用enum
关键字来定义枚举。
应用场景
比如方向 有上下左右; 商品的交易状态 有等待买家付款、买家已付款、卖家已发货以及交易成功; 颜色 有红色、蓝色等; 周 有星期一、星期二....星期日; 月 有一月到12月等等表示有限数据集的都可以使用枚举。 比如方向 有上下左右; 商品的交易状态 有等待买家付款、买家已付款、卖家已发货以及交易成功; 颜色 有红色、蓝色等; 周 有星期一、星期二....星期日; 月有一月到12月等等表示有限数据集的都可以使用枚举。
定义枚举
我们要使用一组相关的命名常量如星期的组合,这时候可以定义一个枚举类型:
ts
// 定义一个枚举类型 Weekday,表示一周的工作日
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
}
数字枚举
数字枚举的特点
数字枚举的特点:枚举变量所对应的值是自动递增的。
- 我们没有初始化枚举类型的值的情况下,
Monday
的值是0、Tuesday
的值是1、Wednesday
的值是2, 而递增到Friday
的值是4。
使用:
-
如果我们对枚举进行初始化
tsenum Weekday { Monday = 10, Tuesday, ... }
Monday
被初始化为10, 因为枚举递增的特性,Tuesday
的值是11,Wednesday
的值是12,递增到Friday
的值是14。初始化的值也可以是负数。
使用枚举
使用枚举也很简单,像对象一样,枚举名.枚举成员。
ts
let today: Weekday = Weekday.Wednesday;
console.log(today); // 输出: 2,因为 Wednesday 在枚举中的索引是 2
每一个枚举成员都可以当作枚举本身进行使用
ts
function getWeekDay(day: Weekday) {
console.log(day)
}
getWeekDay(Weekday.Friday) // 4
枚举成员使用计算值
通过上面的例子我们可以发现,每个枚举成员都有一个与之关联的值,该值可以是常量(如我们Weekday
枚举类型中的Monday
成员就是常量值),但也可以是计算值。计算值是的意思就是枚举的值是被计算出来的。
ts
function getNum() {
return 10
}
enum Weekday {
Monday = getNum(),
Tuesday,
Wednesday,
Thursday,
Friday
}
此时Monday的值就是计算值, 但是上面的代码报错 了,这是因为没有初始化的枚举要么需要放在第一个,要么必须位于使用数字常量或其他常量枚举成员初始化的数字枚举 之后。这里的"其他常量枚举成员初始化的数字枚举"可能有点拗口,其实就是这样:
ts
enum Num {
one = 1,
two = 2
}
enum Weekday {
Monday = Num.two,
Tuesday,
Wednesday,
Thursday,
Friday
}
这里的Monday的值就是其他常量枚举成员初始化的数字枚举值。
字符串枚举
字符串枚举是啥
枚举不仅可以用数字常量初始化,也可以用字符串常量初始化,当一个枚举中成员的值都是字符串,那么这个枚举就是字符串枚举了。
字符串枚举与数字枚举的区别
- 与数字枚举相比, 字符串枚举没有"递增"这个概念了。
ts
enum Weekday {
Monday = "Monday",
Tuesday = "Tuesday",
Wednesday = "Wednesday",
Thursday = "Thursday",
Friday = "Friday"
}
其他
其他的使用与数字枚举一样。
异构枚举
异构枚举是啥
如果枚举成员的值是数字常量和字符串常量混合的枚举类型,那么这个枚举就是异构枚举了 。
ts
enum Weekday {
Monday = '星期一',
Tuesday = '星期二',
Wednesday = '星期三',
Thursday = 1,
Friday = 2
}
这样做看起来很怪异,但或许在某些特殊的场合下会用到, 使用场景极少。
常量枚举值
枚举的值可以是常量也可以是计算值。 常量枚举值指的是在编译期间就可以取到值,计算值则需要在运行期间才能取到值。
Typescript对什么才是常量枚举值有如下的定义:
- 常量文字或者常量字符串
- 对已定义的常量枚举成员的引用
- 对常量枚举值进行
+
,-
,*
,/
,~
%
,<<
,>>
,>>>
,&
,|
,^
算的也是属于常量枚举值
计算枚举值
除了上述三种情况,其他的所有情况都属于计算值, 计算值需要返回一个number。
ts
enum ConstantComputedMember {
A = 1, // constant
B, // constant
C = 'abc', // constant
D = 1 + 1, // constant
F = 'a' + 'b', // constant
G = 1 << 2, // constant
H = A | B, // constant
I = A & B, // constant
J = [1, 2, 3].length, // computed
K = getNum() // computed
}
枚举的编译结果
ts代码最终被编译成js才能运行,那么枚举编译后的结果是什么呢?
数字枚举的编译
ts
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday
}
经过编译后是下面的结果
ts
"use strict";
var Weekday;
(function (Weekday) {
Weekday[Weekday["Monday"] = 0] = "Monday";
Weekday[Weekday["Tuesday"] = 1] = "Tuesday";
Weekday[Weekday["Wednesday"] = 2] = "Wednesday";
Weekday[Weekday["Thursday"] = 3] = "Thursday";
Weekday[Weekday["Friday"] = 4] = "Friday";
})(Weekday || (Weekday = {}));
从上面的编译结果来看,我们不仅能使用Weekday.Monday
得到枚举值0
, 还能通过Weekday[Weekday.Monday]
得到字符串"Monday"
这个键名。这也是枚举的反向映射。
字符串枚举的编译
ts
enum Weekday {
Monday = "Mon",
Tuesday = "Tues",
Wednesday = "Wednes",
Thursday = "Thurs",
Friday = "Fri"
}
经过编译后是下面的结果
ts
"use strict";
var Weekday;
(function (Weekday) {
Weekday["Monday"] = "Mon";
Weekday["Tuesday"] = "Tues";
Weekday["Wednesday"] = "Wednes";
Weekday["Thursday"] = "Thurs";
Weekday["Friday"] = "Fri";
})(Weekday || (Weekday = {}));
从上面的编译结果来看,我们可以通过Weekday.Friday
来获取枚举值Fri
, 但是我们不能用 Weekday[Weekday.Friday]
来获取到字符串Friday
, 这是因为具有字符串值的枚举成员不会生成反向映射。
const 枚举
-
typescript会将常规枚举编译成一个对象与一个自执行的匿名函数,但是创建对象与执行函数需要一定额外的成本, 有没有一种办法枚举只执行枚举的工作,而不产生额外的数据,于是Typescript新增了const 枚举。
-
const 枚举的定义很简单,在常规枚举的定义
enum
前面加上const
关键字即可。const
枚举只能用常量枚举,不能有计算值的枚举成员,而且const
枚举在编译期间完全被删除!
ts
const enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
}
function foo(params: number) {
if (Weekday.Friday === params) {
return 'Friday'
} else {
return 'not Friday'
}
}
foo(Weekday.Friday)
编译后的结果
ts
"use strict";
function foo(params) {
if (4 /* Weekday.Friday */ === params) {
return 'Friday';
}
else {
return 'not Friday';
}
}
foo(4 /* Weekday.Friday */);
枚举Weekday
没有产生额外的对象或函数,使用枚举成员的地方直接编译成了对应的值。
获取枚举键和值的类型
在开发自定义组件的时候,很多组件都会支持颜色属性,我们通常会定义这样的枚举类型。
ts
enum Color {
Red = '#ff0000',
Green = '#00ff00',
Blue = '#0000ff'
}
function changeTheme(color: Color) {
this.color = color
}
但是为了更好的体验,可以允许用户传递进来如red
这样的字符串,这个时候我们可以让我们定义的枚举类型的枚举成员的key
和value
都组装成联合类型。
ts
type KeyTypeColor = Lowercase<keyof typeof Color>
function changeTheme(color: Color | KeyTypeColor) { // color: Color | "red" | "green" | "blue"
this.theme = color
}
changeTheme('red')
扩展
vue3源码中通过枚举加位运算符进行组合权限认证。
源码中定义了ShapeFlags
枚举类型描述了vnode
的类型。
ts
export const enum ShapeFlags {
ELEMENT = 1, // 普通的HTML元素
FUNCTIONAL_COMPONENT = 1 << 1, // 函数式组件
STATEFUL_COMPONENT = 1 << 2, // 有状态组件
TEXT_CHILDREN = 1 << 3, // 文本子节点
ARRAY_CHILDREN = 1 << 4, // 数组子节点
SLOTS_CHILDREN = 1 << 5, // 插槽子节点
TELEPORT = 1 << 6, // teleport组件
SUSPENSE = 1 << 7, // suspense组件
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 需要被keep-live的有状态组件
COMPONENT_KEPT_ALIVE = 1 << 9, // 已经被keep-live的有状态组件
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
// 组件,有状态组件和函数式组件的统称
}
它不仅效率很高,而且很方便的对vnode
进行权限操作。
vnode授权
如上面代码所示COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
。
此时代表COMPONENT
被授予了ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
权限。
vnode鉴权
如果我们要判断一个vnode
是否有COMPONENT
权限,只需要COMPONENT & VnodeType
只要为0就代表没有权限。
ts
const hasPer = ShapeFlags.COMPONENT & ShapeFlags.ELEMENT
console.log(hasPer) // 0
hasPer为0表示没有权限
ts
const hasPer = ShapeFlags.COMPONENT & ShapeFlags.FUNCTIONAL_COMPONENT
console.log(hasPer) // 2
hasPer不为0表示有权限。
总结
相信通过此文你已经掌握枚举了。