3、TS 中的位枚举(枚举的位运算)

483 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

之前在学习 Linux 时, 发现对用户对文件的操作权限是使用 10进制 的数字表示的,其实在 TS 中的数字枚举也支持类似的功能。

位枚举(枚举的位运算)

主要是针对 数字枚举

举个栗子

有这样一种场景

创建一个用户权限的枚举,使用 数字枚举 进行约束。

enum Permissions {
    Read, //可读权限
    Write, //可写权限
    Create, //可以创建权限
    Delete, //可以删除权限
}

这就创建了一个 权限枚举, 但是存在一些问题

假如我需要对权限进行组合, 一个用户 有可读、可写的权限,没有创建、删除权限, 那该怎么完成权限的组合呢?

权限组合

方法一

可以给枚举添加跟多的权限名

enum Permissions {
    Read, //可读权限
    Write, //可写权限
    Create, //可以创建权限
    Delete, //可以删除权限
    ReadWrite, //可读可写权限
    ...
}

这种方法可以解决问题,但是书写很麻烦, 权限的情况种类太多, 之后增加新的枚举字段 不容易扩展 不推荐使用

方法二

利用 TS 的枚举位运算 (本质也是使用 js 的能力)

解析

enum Permissions {
    Read = 1, //可读权限
    Write = 2, //可写权限
    Create = 4, //可以创建权限
    Delete = 8, //可以删除权限
}

相信数学比较好的同学能发现规律, 1、2、4、8 都是 2^n, 把它们转化为二进制。

十进制二进制权限
10001可读
20010可写
40100可创建
81000可以删除

其中只有一个 1 其余都是 0

此时就可以使用 十进制权限想加来表示权限

举个栗子

一个用户的权限为 3, 3的二进制为 0011, 对应上面的表格, 该用户拥有 可读,可写权限。

因此可以通过基本权限来组成新的权限

如何组合权限

用户有可读、可删除的权限, 怎么使用 TS 表达出来呢?

//使用位运算
const readAndDelete: Permissions = Permissions.Read | Permissions.Delete

位运算 两个数字换算成二进制后进行的运算,产生一个新的二进制

| 表示 或运算 : 将两个二进制进行比较,两个二进制在相同的 位置上 其中有一个是 1 产生新的二进制该位也就是 1,否则是 0

举个栗子

1 和 8进行位运算,将十进制转化为 二进制为 0001 1000

十进制3位2位1位0位
10001
81000
结果1001

位运算之后的结果为 1001 转为十进制为 9, 所以 9 可以表示 可读、可删除的权限。

如何判断是否拥有某个权限

如果用户的权限为 10 怎么判断拥有某一个权限

原理: 判断二进制在用一位上 是否都是 1 的结果和权限判断是否相等。

/**
 * 判断 target 时候拥有 per 权限
 * @param target 目标
 * @param per 判断值
 */
function hasPermissions(target: Permissions, per: Permissions): boolean {
    return (target & per) === per
}
hasPermissions(10, Permissions.Read) //false 没有可读权限
hasPermissions(10, Permissions.Write) //true 有可写权限

&  表示 且运算:将两个二进制进行比较,两个二进制在相同的 位置上 其中都为 1 产生新的二进制该位也就是 1,否则是 0

画图解释

image.png

如何删除某个权限

如果用户的权限为 10 怎么删除该用户的可读权限

原理: 判断二进制在用一位上 是否都是 1 如果是代表拥有这个权限。

/**
 * target 删除 per 权限
 * @param target 目标
 * @param del 删除的权限值
 */
function deletsPermissions(target: Permissions, del: Permissions) {
    return target ^ del
}
let p = 10;
p = deletsPermissions(p, Permissions.Write); //删除可写权限
console.log(hasPermissions(p, Permissions.Write)); //false

^  表示 异或运算:将两个二进制进行比较,两个二进制在相同的 位置上 相等, 产生新的二进制该位也就是 0,否则是 1

画图解释

image.png

总结

在前端可能不会使用到此手段, 如果某一天需要处理权限的问题请想起它, 它十分优雅其极度具有扩展性。