枚举
使用枚举可以定义某些常量的值。
使用字面量类型和联合类型,也能达到同样的效果。
js
let gender: '男' | '女';字面量类型的问题
有枚举的出现,就代表了有字面量类型无法解决的问题:
- 在类型约束的位置,会产生重复的代码。可以通过类型别名解决
js
let gender: '男' | '女';
function getGender(g: '男' | '女') {}
// 通过类型别名解决
type Gender = '男' | '女';
let gender: Gender;
function getGender(g: Gender) {}- 逻辑含义和真实值会产生混淆,导致当修改真实值的时候,产生大量的修改
js
type Gender = '男' | '女';
let gender: Gender = '男';
gender = '女';假设此时有大量的赋值代码,如果要将性别的字面量改成 帅哥/美女 或 male/female,就会产生大量的修改,而 男/女、帅哥/美女 、male/female它们是逻辑含义相同的。
- 字面量类型不会进入编译结果
如果有这样一个需求,将性别的取值范围显示到页面中,这是做不到的,因为 typescript 不参与运行,只参与编译。
定义一个枚举
格式:enum 枚举名称 { 枚举成员1 = 值1, 枚举成员2 = 值2, ... }
js
enum Gender {
male = '男',
female = '女'
}
let gender: Gender;
gender = Gender.male;上述代码就解决了字面量类型的第二个问题,我们只需要使用枚举.枚举成员进行赋值,直接修改枚举成员就可以达到修改真实值的效果。
而第三个问题,枚举自动就帮我们解决了,因为枚举最终是会进入到编译结果的。
js
enum Gender {
male = '男',
female = '女'
}
function printGender() {
const values = Object.values(Gender);
values.forEach(v => console.log(v));
}
printGender();枚举的规则
- 枚举成员的值只能是字符串或数字
- 数字枚举成员的值会自动递增
- 被数字枚举约束的变量,可以直接赋值为数字
- 数字枚举的编译结果和字符串枚举的编译结果有差异
js
enum Level {
level1,
level2,
level3
}
// 数字枚举的编译结果
var level = {
level1: 0,
level2: 1,
level3: 2,
0: 'level1',
1: 'level2',
2: 'level3'
}枚举的最佳实践
- 尽量不要在一个枚举中同时出现字符串和数字
- 使用枚举时,尽量使用枚举字段的名称,而不使用真实值
枚举的位运算
看到位运算,就想到这是针对数字,而不是字符串。
这里我们通过设计一个权限的枚举来举例。
首先定义一下权限枚举,通常就是增删改查。
js
enum Permission {
Read,
Write,
Create,
Delete
}接着就是对权限枚举的应用,通常是三种:
- 如何组合权限?如,可读可写的权限
- 如何判断是否拥有某个权限?如,现在拥有可读可写的权限,判断是否拥有可读权限
- 如何删除某个权限?如,现在拥有可读可写的权限,需要删除可写权限
如何组合权限?
很明显,我们不能把组合的权限写进枚举中,一旦有变动,修改量巨大。
我们可以这样设计:
js
enum Permission {
Read = 1, // 2^0 -> 0001
Write = 2, // 2^1 -> 0010
Create = 4, // 2^2 -> 0100
Delete = 8 // 2^3 -> 1000
}上面的规律可以看到每个权限都是 2 的 n 次方,将它们转换成二进制,可以看出它们分别在其中一位中为 1,其余为 0。那么我们可以这样认为,(从右往左看)第一位为 1 就说明拥有可读权限,第二位为 1 就说明有可写权限,以此类推。
举个例子,3 的二进制是 0011,我们就可以联想到这是可读可写的权限组合。
那么如何组合呢?通过位运算就可以实现了。
位运算中的与运算(符号为 |),它的特点就是同0为0。
js
// 0001
// 0010
// ----
// 0011
let p: Permission = Permission.Read | Permission.Write; // 3如何判断是否拥有某个权限?
通过位运算中的与运算(符号为 &),它的特点就是同1为1。
js
/**
* 与运算,同1为1
* 0011
* 0001
* ----
* 0001
* 判断是否拥有某个权限
* @param target 判断目标
* @param p 某个权限
*/
function hasPermission(target: Permission, p: Permission) {
return (target & p) === p;
}
let target: Permission = Permission.Read | Permission.Write; // 3
const result = hasPermission(target, Permission.Read); // true如何删除某个权限?
通过位运算中的异或运算(符号为 ^),它的特点就是同0异1。
js
/**
* 异或运算,同0异1
* 0011
* 0010
* ----
* 0001
* 删除某个权限
* @param target 删除目标
* @param p 要删除的权限
*/
function deletePermission(target: Permission, p: Permission) {
return target ^ p;
}
let target: Permission = Permission.Read | Permission.Write; // 3
const result = deletePermission(target, Permission.Write); // 1