Skip to content

枚举

使用枚举可以定义某些常量的值。

使用字面量类型和联合类型,也能达到同样的效果。

js
let gender: '男' | '女';

字面量类型的问题

有枚举的出现,就代表了有字面量类型无法解决的问题:

  1. 在类型约束的位置,会产生重复的代码。可以通过类型别名解决
js
let gender: '男' | '女';
function getGender(g: '男''女') {}

// 通过类型别名解决
type Gender = '男''女';
let gender: Gender;
function getGender(g: Gender) {}
  1. 逻辑含义和真实值会产生混淆,导致当修改真实值的时候,产生大量的修改
js
type Gender = '男' | '女';

let gender: Gender = '男';
gender = '女';

假设此时有大量的赋值代码,如果要将性别的字面量改成 帅哥/美女male/female,就会产生大量的修改,而 男/女帅哥/美女male/female它们是逻辑含义相同的。

  1. 字面量类型不会进入编译结果

如果有这样一个需求,将性别的取值范围显示到页面中,这是做不到的,因为 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
}

接着就是对权限枚举的应用,通常是三种:

  1. 如何组合权限?如,可读可写的权限
  2. 如何判断是否拥有某个权限?如,现在拥有可读可写的权限,判断是否拥有可读权限
  3. 如何删除某个权限?如,现在拥有可读可写的权限,需要删除可写权限

如何组合权限?

很明显,我们不能把组合的权限写进枚举中,一旦有变动,修改量巨大。

我们可以这样设计:

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