使用二进制设计课堂权限
使用二进制能很方便的表达和计算(组合、切换和校验)权限,以 Linux 文件权限为例。
| 权限 | 字母表示 | 数字表示 | 二进制 |
|---|---|---|---|
| 读 | r | 4 | 0b100 |
| 写 | w | 2 | 0b010 |
| 执行 | x | 1 | 0b001 |
权限之间可组合:
1 | const 读写 = 0b100 | 0b010 // 6 即 0b110 |
总的组合结果共 C(2,1) * C(2,1) * C(2,1),8 种,无权限 0 (---),读权限 4 (r--),写权限 2 (-w-),执行权限 1 (--x),读和写权限 6 (rw-),读和执行权限 5 (r-x),写和执行权限 3 (-wx),读、写和执行权限 7 (rwx)。
二进制运算符
二进制位运算符包括 |(按位或 OR)、&(按位与 AND)、^(按位异或 XOR)、~(按位非,取反 NOT)、<<(左移 Left shift)、>>(有符号右移)、>>>(无符号右移)。
JavaScript 位运算是基于 32 位整数的,会先把 64 位浮点数转换为 32 位整数计算,计算完成后再将 32 位转为 64 位。
|运算符
| 运算将两个操作数的每个对应位进行或运算,结果中,两个操作数对应位上至少有一个为 1 时才为 1,否则为 0(相当于求并),即 1 | 1 = 1、0 | 0 = 0、0 | 1 = 1。
1 | 1100 |
&运算符
& 运算将两个操作数的每个对应位进行与运算,结果中,两个操作数对应位上都为 1 时才为 1,否则为 0,即 1 & 1 = 1、0 & 0 = 0、0 & 1 = 0。以操作数 0010 为例:
1 | xxyx |
上述例子中 x 位为任何数,& 运算的结果都是 0,最终结果只受 y 位影响,当 y = 0 时,结果为 0000,y = 1 时,结果为 0010。即如果 a & b === b 为 true,则说明 a 包含 b。
^运算符
^ 运算将两个操作数的每个对应位进行异或运算,结果中,两个操作数对应位上不相同时才为 1,相同时为 0。利用这个特点,可实现 Toggle 计算。
1 | 0001 |
1 | 0011 |
~运算符
~ 将操作数的每一位取反。有点类似于反码,但不同的是,~ 会将符号位也取反,而取反码,符号位不变。
1 | 1010 |
以 ~10 (~0b1010) 为例,其 32 位二进制为 00000000000000000000000000001010 (正数的补码就是原码),~ 取反得到 11111111111111111111111111110101,由于符号位是 1,所以这是一个负数,而计算机中存储负数是以补码的方式来存储的,所以对补码求原码再转成十进制即可,对补码求原码就是使用此补码再求一遍补码,也就是先取反码再补 1,即 10000000000000000000000000001010 加 1,结果为 10000000000000000000000000001011,即 -11。
~-2 的 32 位二进制为 11111111111111111111111111111110(负数的补码需要反码再加 1),~ 取反得到 00000000000000000000000000000001,再将结果转为十进制,即 1。
任何数字 x 的按位非运算结果都是 -(x + 1)。例如,~-5 运算结果为 4。
<<左移运算符
将数值的二进制码向左移动一定的位(< 32),右边用 0 填充。
1 | 1010 >> 1 |
使用左移还可用来取整。位运算操作的是整数,忽略小数部分,等同于数值的整数部分,左移 0 位,结果还是整数部分。
1 | 1.111 << 0 // 1 |
>>右移运算符
将数值的二进制码向右移动一定的位(<32),遗弃被丢出的位。
1 | 1010 >> 1 |
课堂权限设计
定义课堂黑板权限为 1、语音权限为 2、视频权限为 4。权限间可组合,同时拥有黑板、语音和视频权限 Board | Audio | Video,即 0111。使用左移 << 定义权限、使用按位或 | 组合权限、使用按位异或 ^ 切换权限(添加或删除权限)、使用按位与 & 校验权限。
1 | // << 定义权限 |
注:如果只是单纯的添加权限,可以使用按位或 |,单纯的删除权限,则可以先取反,再执行与操作 &(~feature)。
1 | student.permission = student.permission &(~Permission.Audio) |