当前位置:嗨网首页>书籍在线阅读

16-位运算符

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

5.9.1 位运算符

位运算符允许在数字的每个二进制位上执行操作。如果读者没有类似C语言这样偏底层的编程语言经验,或者对计算机内部如何存储数字不太了解,可能需要先了解相关的知识(可以跳过这部分内容,因为很少有应用使用到位运算符)。位运算符将其操作数当成二进制补码格式的32位有符号整型数字。因为在JavaScript中,所有的数字都是双精度的,JavaScript在执行位运算前会先将数字转换成32位的整型,并在返回结果之前转换回来。

位运算符与逻辑运算符的共同点在于它们也是执行逻辑操作(与、或、非、异或),不同的是它们在整型的每一个二进制位上进行运算。如表5-7所示,它们还包含了能将二进制位进行位移的位移运算符。

表5-7  位运算符

| 运 算 符 | 描  述 | 示  例 | | :----- | :----- | :----- | :----- | :----- | | & | 位与 | 0b1010 & 0b1100 // 结果: 0b1000 | | | | 位或 | 0b1010 | 0b1100 // 结果: 0b1110 | | ^ | 位异或 | 0b1010 ^ 0b1100 // 结果: 0b0110 | | ~ | 位非 | ~0b1010 // 结果: 0b0101 | | << | 左移位 | 0b1010 << 1 // 结果: 0b10100 | 0b1010 << 2 // 结果: 0b101000 | | >> | 符号传播右移位 | (参见下文) | | >>> | 补零右移位 | (参见下文) |

注意,左移位实际上是乘以2,右位移则是除以2然后舍去尾数。

存在两种补码,最左边的二进制位为1时表示负数,0表示正数,它们都可以执行右移位。以−22为例。如果想要获取二进制表示法,以正数22开始,取反(反码)再加1(补码)。

let n = 22        //  32位二进制数:     00000000000000000000000000010110
n >> 1            //                   00000000000000000000000000001011
n >>> 1           //                   00000000000000000000000000001011
n = ~n            // 取反:              11111111111111111111111111101001
n++               // 加1:              11111111111111111111111111101010
n >> 1            //                   11111111111111111111111111110101
n >>> 1           //                   01111111111111111111111111110101

除非从事的是硬件编程,或者想更好理解数字在计算机内部的表示原理,否则几乎用不上位运算符(一般被戏称为“玩位(味)”)。一个与硬件无关的使用场景可能是用二进制位来高效存储“标记位”(布尔值)。

例如,考虑一个Unix风格的文件权限:读、写和执行。一个给定的用户可以任意组合这三种权限,以达到预期目标。因为存在三种标志,所以需要三个二进制位来存储这些信息。

const FLAG_READ 1             // 0b001
const FLAG_WRITE 2            // 0b010
const FLAG_EXECUTE 4          // 0b100

有了位运算符,可以在一个单一的数值中组合、切换,以及检测每一个二进制位标记。

let p = FLAG_READ | FLAG_WRITE;           // 0b011
let hasWrite = p & FLAG_WRITE;            // 0b010 - truthy
let hasExecute = p & FLAG_EXECUTE;        // 0b000 - falsy
p = p ^ FLAG_WRITE;                       // 0b001 – 写标记开关 (关闭)
p = p ^ FLAG_WRITE;                       // 0b011 – 写标记开关 (开启)
// 我们甚至可以确定在一个表达式中确定多个标记
const hasReadAndExecute = p & (FLAG_READ | FLAG_EXECUTE);

注意常量 hasReadAndExecute ,这里不得不使用分组运算符,因为与运算符比或运算符的优先级高,所以用括号来强制优先执行或运算。