位操作符用于数值的底层操作,ECMAScript中的所有数值都一样64位格式存储,但位操作并不直接应用到64位表示,而是先把值转换为32位整数,再进行位操作,之后再把结果转换为64位,对于开发者而言,只需要考虑32位整数即可
有符号整数使用32位的前31位表示整数值,第32位表示数值的符号,0表示正,1表示负。这一位称为符号位,它的值决定了数值其余部分的格式
如何将二进制转为十进制
数值18的二进制格式为00000000000000000000000000010010
,精简: 10010
,后者是用到了5个有效位,决定了实际的值
负值以一种称为二补数(补码)的二进制编码存储,一个数值的二补数通过三个步骤计算得到:
- 确定绝对值的二进制表示(如:-18,先确定18的二进制表示)
- 找到数值的一补数(或反码),换句话说,就是每个0都变成1,每个1都变成
- 给结果加1
基于上述步骤确定-18的二进制表示,首先从18的二进制表示开始
0000 0000 0000 0000 0000 0000 0001 0010
然后,反转每一位的二进制值
1111 1111 1111 1111 1111 1111 1110 1101
最后,给一补数加1
1111 1111 1111 1111 1111 1111 1110 1101
1
----------------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110
那么结果:-18的二进制表示就是11111111111111111111111111101110
ECMAScript会帮我们记录这些信息,在把负值输出为一个二进制字符串时,我们会得到一个签名加了减号的绝对值
let num = -18;
console.log(num.toString(2)); // '-10010'
在将-18转换为二进制字符串时,结果得到-100,转换过程会求得二补数,然后再以更符合逻辑的形式表示出来
注意: 默认情况下,
ECMAScript中的所有整数都表示为有符号数
。
但其实存在无符号整数,对于无符号整数来说,第32位不表示符号,因为只有正值,无符号整数比有符号整数的范围更大,因为符号位被用来表示数值
注意
特殊值: NaN、 Infinity 在位操作中都会被当成0处理
非数值: 首先隐式使用Number函数将该值转换为数值,然后应用位操作,最终结果为数值
按位非
按位非操作符使用波浪符
~
表示,它的作用是返回数值的一补数
let num1 = 25; // 二进制 00000000000000000000000000011001
let num2 = ~num; // 二进制 11111111111111111111111111100110
console.log(num2); // -26
由此可看出:按位非的最终效果是对数值取反并减1
按位与
按位与使用和号
&
表示,有两个操作数,本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作
第一数值的位 | 第二数值的位 | 结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
按位与操作在两个位都是1时返回1,在任何一位是0时返回0
栗子:对数值25与3 进行与操作
let result = 25 & 3;
console.log(result); // 1
计算过程:
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001
按位或
按位或操作符用管道符
|
表示,使用两个操作符,按位或遵循如下真值表
第一数值的位 | 第二数值的位 | 结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
按位或操作在失少一位是1时返回1,两位都是0时返回0
栗子:
let result = 25 | 3;
console.log(result); // 27
在参与计算的两个数中,有 4位都是 1,因此它们直接对应到结果上。二进制码 11011等于 27
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011
按位异或
按位异或使用脱字符
^
表示,同样有两个操作符,真值表
第一数值的位 | 第二数值的位 | 结果 |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
按位异或与按位或的区别:它只在一位上是1的时候返回1(两位都是1或者0,则返回0)
栗子:
let result = 25 ^ 3
console.log(result); // 26
25 = 0000 0000 0000 0000 0000 0000 0001 1001
3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
注意:对于同样两个值:执行按位或与 比 执行按位或操作得到的结果小1
左移
左移操作符用两个小于号
<<
表示,会按照指定的位数将数值的所有位向左移动
例如:数值2(二进制10)向左移5位,就会得到64(二进制1000000)
let oValue = 2; // 等于二进制 10
let nValue = oValue << 5 // 等于二进制 100000,即十进制64
在移位后,数值右端会空出5位,左移会以0填充这些空位,让结果是完整的32位数值
image.png
注意:左移会保留它所操作数值的符号,比如:如果-2左移5位,将得到-64,而不是正64
有符号右移
有符号右移由两个大于号(>>)表示,会将数值的所有32位都想右移,同时保留符号(正或负)。
有符号右移实际上是左移的逆运算
栗子:如果将64右移5位,那就是2
let oValue = 64; // 等于二进制 1000000
let nValue = 64 >> 5; // 等于二进制 10 即十进制2
image.png
位移后会出现空位,右移后的空位会出现在左侧,且在符号位之后,ECMAScript会用符号位的值来填充这些空位(即正数补零,负数补1)
, 以得到完整的数值
无符号右移
无符号右移用3个大于号表示
>>>
, 会将数值的所有32位都向右移动,对于正数,无符号右移与有符号右移结果相同
栗子:64向右移动5位,会变成2
let oValue = 64; // 等于二进制 1000000
let nValue = oldValue >>> 5; // 等于二进制 10 即十进制2
对于负数,无符号右移操作符将负数的二进制表示当成正数的二进制来表示处理,因为负数是其绝对值的二补数,所以右移之后结果变得非常大
let oValue = -64; // 等于二进制 11111111111111111111111111000000
let nValue = oValue >>> 5; // 等于十进制 134217726
在对-64无符号右移5位后,结果是134 217 726,
这是因为-64的二进制表示:1111111111111111111 1111111000000
无符号右移却将它当成正值,也就是: 4 294 967 232
把这个值右移5位后,结果:00000111111111111111111111111110
即: 134 217 726
-- 来源<JavaScript高级程序设计第四版>
位运算符的用途
- 用于判断一个数的奇偶
// %
let num = 1;
let isEven = num % 2 === 0
// &
let isEvent = num & 0 === 0; // 判断是否是偶像 位操作符比 % 要快
- 利用 ^ 完成值的交换
let a = 1,
b= 2;
2.1 第一种: 使用temp
let temp = a;
a = b;
b = temp;
2.2 第二种: 使用解构
[a, b] = [b, a]
2.3 第三种:使用 ^
a ^= b
b ^= a
a ^= b
网友评论