在位运算里面我们需要了解什么是数值的底层操作,也就是操作内存中表示数据的比特(位)。ECMAScript 中的所有数值都以 IEEE 754 64 位格式存储,但位操作并不直接应用到 64 位表示,而是先把值转换为 32 位整数,再进行位操作,之后再把结果转换为 64 位。对开发者而言,就好像只有 32 位整数一样,因 为 64 位整数存储格式是不可见的。既然知道了这些,就只需要考虑 32 位整数即可。
在有符号的整数里面,前面31位表示整数值,最后一位(32位)表示数值的符号:0表示正,1表示负。。比如,数值18的二进制格式为00000000000000000000000000010010, 或更精简的 10010。
负值是一种二补数(补码)的二进制编码存储的,一个二补数可以通过下面三个步骤得到:
- 首先确定该数值绝对值的二进制表示(比如-20,我们先确定20的二进制表示)
- 找到数值的补数(反码),也就是把每个0变为1,1变成0
- 最后,在最后一位加上1
注意:在处理有符号的整数时,我们无法访问第31位。
接下来我们就开始了解位运算操作符:
1.按位非
按位非操作符我们用:~
表示,它的作用是,返回数值的一补数:
let num1 = 25; // 二进制 00000000000000000000000000011001 let num2 = ~num1; // 二进制 11111111111111111111111111100110 console.log(num2); // -26
这里我们可以看到,按位非操作符最后结果就是数值取相反数并减一。
let num1 = 25; let num2 = -num1 - 1; console.log(num2);
虽然这两段代码最后结果是一样的,但是前面那段代码更快,因为位操作符是数值底层进行操作的。
2.按位与
按位与操作与我们用:&
表示,它的本质就是将两个数的每一位二进制对齐,然后根据下面表格的规则进行操作:
第一个数值的位 | 第二个数值的位 | 结果 |
1 | 1 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
0 | 0 | 0 |
按位与操作在两个位都是 1 时返回 1,在任何一位是 0 时返回 0。
3.按位或
按位或操作符我们用: |
表示,同样也是将两个数的二进制每一位对齐,然后根据规律进行计算,规律如下:
第一个数值的位 | 第二个个数值的位 | 结果 |
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
按位或操作在至少一位是 1 时返回 1,两位都是 0 时返回 0。
4.按位异或
按位异或操作符我们用: ^
表示,计算方法同上,先对齐,在根据规律进行计算。
第一个数值的位 | 第二个个数值的位 | 结果 |
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)。
5.左移
左移操作符我们用: <<
表示,按照指定的位数将数值向左移动,右边空缺的位数用0补齐。例如: 2 << 5: 00000000000000000000000000000010
-->
00000000000000000000000001000000
注意:对于负数的位移操作,我们会保留它的符号,也就是说-2左移5位。我们会得到-64,而不是64
6. 有符号右移
有符号又位移我们用: >>
表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。 有符号右移实际上是左移的逆运算。
例如:64>>5:
00000000000000000000000001000000
-->
00000000000000000000000000000010
同样的在又移之后,左边出现的空位由0补齐。(但是符号位不算在位移位中
)
7. 无符号右移
无符号位移操作符我们用: >>>
表示,会将数值的所有 32 位都向右移。对于正数,无符号右移与 有符号右移结果相同。仍然以前面有符号右移的例子为例,64 向右移动 5 位,会变成 2。
但是对于负数就不一样了,与有符号右移不同,无符号右移会给空位补 0,而不管符号位是 什么。对正数来说,这跟有符号右移效果相同。但对负数来说,结果就差太多了。无符号右移操作符将负数的二进制表示当成正数的二进制表示来处理。因为负数是其绝对值的二补数,所以右移之后结果变 得非常之大(符号位算在位移位中
),如下面的例子所示:
let oldValue = -64; // 等于二进制11111111111111111111111111000000 let newValue = oldValue >>> 5; // 等于十进制134217726
在对64 无符号右移 5 位后,结果是 134 217 726。这是因为64 的二进制表示是 1111111111111111111 1111111000000,无符号右移却将它当成正值,也就是 4 294 967 232。把这个值右移 5 位后,结果是 00000111111111111111111111111110,即 134 217 726