前面看到MDN对数组 find
polyfill
方法中使用了 o.length >>> 0
三个 >>>
的运算,看都看了那就顺便梳理下 javaScript 中的位运算符和实际的一些运用场景。
概述
- 所有的按位操作符的操作数都会被转成
补码
(two’s complement)形式的有符号32位整数。 - 超过32位的数字会被丢弃,不足的通过 0 补齐。
这里先简单介绍下:原码
、反码
、补码
的概念。
原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号(0为正,1为负), 其余位表示值。
例如:
314
为:00000000000000000000000100111010
-314
为10000000000000000000000100111010
反码
- 正数的反码是其本身
- 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
例如:
314
为:00000000000000000000000100111010
-314
为11111111111111111111111011000101
补码
- 正数的补码就是其本身
- 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
例如:
314
为:00000000000000000000000100111010
-314
为11111111111111111111111011000110
临界值:
- (2 ** 31)
可表示最小值。是除了最左边为1外,其他比特位都为0的整数。10000000000000000000000000000000
(2 ** 31) - 1
可表示最大值。是除了最左边为0外,其他比特位都为1的整数。01111111111111111111111111111111
位运算符
& 按位与(AND)
对每对比特位执行与(AND)操作, 都是 1 时结果为 1 ,否则为 0
1 | // (9).toString(2) = 1001 |
使用
可用来判断奇偶数,偶数 & 1 = 0
、奇数 & 1 = 1
1 | console.log(2 & 1) // 0 |
| 按位或(OR)
对每一对比特位执行或(OR)操作, 如果 a 或 b 为 1 ,则结果为1,否则为 01
2
3 9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)
^ 按位异或(XOR)
对每一对比特位执行异或(XOR)操作。当 a 和 b 不相同时,a XOR b 的结果为 1,否则为 01
2
3 9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)
~ 按位非(NOT)
对每一个比特位执行非(NOT)操作。NOT a 结果为 a 的反转(即反码)1
2 9 (base 10) = 00000000000000000000000000001001 (base 2)
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)
使用
常见的和 indexOf
一起使用,-1
32位编码为 11111111111111111111111111111111
,取按位非后就为 0
1 | var str = 'rawr'; |
<< 左移(Left shift)
将 a 的二进制形式向左移 b (< 32) 比特位,右边用0填充。
例如, 9 << 2 得到 36:1
2 9 (base 10): 00000000000000000000000000001001 (base 2)
9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)
在数字 x 上左移 y 比特得到
x * (2 ** y)
.
>> 有符号右移
将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,拷贝最左侧的位以填充左侧。
例如, 9 >> 2 得到 2:1
2 9 (base 10): 00000000000000000000000000001001 (base 2)
9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)
相比之下, -9 >> 2 得到 -3,因为符号被保留了。1
2 -9 (base 10): 11111111111111111111111111110111 (base 2)
-9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)
由于新的最左侧的位总是和以前相同,符号位没有被改变。所以被称作“符号传播”。
使用
&
, >>
, |
来完成rgb值和16进制颜色值之间的转换
1 | /** |
>>> 无符号右移
将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。
对于非负数,有符号右移和无符号右移总是返回相同的结果。例如 9 >>> 2 和 9 >> 2 一样返回1
2 9 (base 10): 00000000000000000000000000001001 (base 2)
9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)
因为符号位变成了 0,所以结果总是非负的。(译注:即便右移 0 个比特,结果也是非负的。)
但是对于负数却不尽相同1
2 -9 (base 10): 11111111111111111111111111110111 (base 2)
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)
总结
实际的开发中尽量不要使用,不易读和理解,除非是在一些极致追求性能的场景用用吧。