JavaScript的位运算符是将进行运算的数字(八进制、十进制、十六进制等)转换为32位的二进制串,超过 32 位的数字会丢弃其最高有效位,只保留后32位二进制串。然后再对每一位进行运算。但运算后得出的结果,会再次转换成标准的十进制的数字,然后返回。
注意: 如果进行位运算的数字是负数,则我们需要使用该负数的32位二进制补码来进行位运算。同理,如果运算后的结果为正数,则运算后的二进制串就是结果数值的二进制形式,如果运算后的结果为负数,则运算后的二进制串是结果数值的二进制补码形式。
位运算符通常在处理大整数、位掩码、图形、加密等情况下使用,在日常编程工作中并不常用,对于一般的数字运算,使用常规的算术运算符即可。
二进制补码是一种表示有符号整数的方法。在计算机中,整数通常以二进制形式存储和处理。补码表示法通过将负数转换为正数的补码形式,使得加法和减法操作可以在计算机中使用相同的硬件电路进行处理。
在二进制补码中,最高位用于表示符号位,0表示正数,1表示负数。对于正数,其补码与其原码相同。而对于负数,其补码的计算方式如下:
① 将负数的绝对值转换为二进制原码形式。
② 对该二进制数取反(即将0变为1,将1变为0)。
③ 对取反后的二进制数加1。
例如,假设我们要表示-5
的补码。首先,将-5的绝对值转换为二进制,得到 00000101
。然后对其取反,得到 11111010
。最后,对取反后的二进制数加1,得到 11111011
,这就是-5
的补码表示。如果想要将补码转换为原数值,只需要倒序进行操作即可。
// 声明一个十六进制变量
let a = 0x10;
// 声明一个十进制变量
let b = 10;
// 声明一个八进制变量
let c = 0o22;
// 获取a、b、c的二进制
console.log(a.toString(2)); // 10000
console.log(b.toString(2)); // 1010
console.log(c.toString(2)); // 10010
&
(按位与) 该运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行&
(与)操作。如果对应位的两个数字都为1
,则该位运算的结果就为1
,否则为0
。
// a、b、c 两两进行按位与运算
console.log(a & b); // 10000 & 1010 = 00000 = 0
console.log(b & c); // 1010 & 10010 = 00010 = 2
console.log(a & c); // 10000 & 10010 = 10000 = 16
// 负数进行按位与运算
console.log(-5 & 5); // 11111011 & 00000101 = 00000001 = 1
console.log(-10 & -18); // 11110110 & 11101110 = 11100110(补码) = -26
|
(按位或) 该运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行|
(或)操作。如果对应位的两个数字中存在一个以上的1
,则该位运算的结果就为1
,否则为0
。
// a、b、c 两两进行按位或运算
console.log(a | b); // 10000 | 1010 = 11010 = 26
console.log(b | c); // 1010 | 10010 = 11010 = 26
console.log(a | c); // 10000 | 10010 = 10010 = 18
// 负数进行按位或运算
console.log(-5 | 5); // 11111011 | 00000101 = 11111111(补码) = -1
console.log(-10 | -18); // 11110110 | 11101110 = 11111110(补码) = -2
^
(按位异或) 该运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行^
(异或)操作。如果对应位的两个数字不相同,则该位运算的结果就为1
,否则为0
。
// a、b、c 两两进行按位异或运算
console.log(a ^ b); // 10000 ^ 1010 = 11010 = 26
console.log(b ^ c); // 1010 ^ 10010 = 11000 = 24
console.log(a ^ c); // 10000 ^ 10010 = 10 = 2
// 负数进行按位异或运算
console.log(-5 ^ 5); // 11111011 ^ 00000101 = 11111110(补码) = -2
console.log(-10 ^ -18); // 11110110 ^ 11101110 = 00011000 = 24
~
(按位非) 该运算符表示将要进行运算的数字,先转换成32位的二进制串,再按位进行~
(非)操作,也就是反转每一位,如果原位为1
,则反转为0
;如果原位为0
,则反转为1
。
但是要注意,在JavaScript中32 位有符号整数操作数根据补码运算规则进行反转,也就是说,最高有效位表示负数。
在进行按位非运算时,任何数字 x
的运算结果都是 -(x + 1)
,例如,~-5
运算结果为 4
。由于数字 ~-1
和 ~4294967295
(2 ** 32 - 1)均使用 32 位表示形式,它们的运算结果均为 0
。
// 进行按位非运算
console.log(~a); // ~10000 = -17
console.log(~b); // ~1010 = -11
console.log(~c); // ~10010 = -19
// 负数进行按位非运算
console.log(~-5); // ~1111011(补码) = 0000100 = 4
console.log(~-10); // ~11110110(补码) = 00001001 = 9
<<
(左移) 该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向左移动运算符右侧数字的位数。由于向左移动,左边超出32的位数将会被清除,右边会空出位,填充0
。
移动任意数字 x << y
位,得出 x * 2 ** y
。 例如:9 << 3
等价于 9 * 2³ = 9 * 8 = 72
。
// 进行左移运算
console.log(a << 1); // 10000 << 1 = 100000 = 32
console.log(b << 2); // 1010 << 2 = 101000 = 40
console.log(c << 3); // 10010 << 3 = 10010000 = 144
// 负数进行左移运算
console.log(-5 << 1); // 11111011(补码) << 1 = 11110110(补码) = -10
console.log(-10 << 2); // 11110110(补码) << 2 = 11101100(补码) = -40
>>
(算术右移) 该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。由于向右移动,右侧被移出的位会被丢弃,左侧空出的位会根据最左侧的符号位进行填充。
因此负数进行算术右移的结果一定为负数,正数进行算术右移的结果一定为正数。
// 进行算术右移运算
console.log(a >> 1); // 10000 >> 1 = 01000 = 8
console.log(b >> 2); // 1010 >> 2 = 0010 = 2
console.log(c >> 3); // 10010 >> 3 = 00010 = 2
// 负数进行算术右移运算
console.log(-5 >> 1); // 11111011(补码) >> 1 = 11111101(补码) = -3
console.log(-10 >> 2); // 11110110(补码) >> 2 = 11111101(补码) = -3
>>>
(无符号右移) 该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。由于向右移动,右侧被移出的位会被丢弃,左侧空出的位填充0
。
因此无论正数还是负数,进行无符号右移的结果一定为非负数。
// 进行无符号右移运算
console.log(a >>> 1); // 10000 >>> 1 = 1000 = 8
console.log(b >>> 2); // 1010 >>> 2 = 10 = 2
console.log(c >>> 3); // 10010 >>> 3 = 10 = 2
// 负数进行无符号右移运算
console.log((-5 >>> 0).toString(2));
console.log(-5 >>> 1); // 11111111111111111111111111111011(补码) >>> 1 = 0111111111111111111111111111101 = 2147483645
console.log(-10 >>> 2); // 11111111111111111111111111110110(补码) >>> 2 = 00111111111111111111111111111101 = 1073741821
&=
(按位与赋值) 该组合运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行&
(与)操作。然后再将运算后的结果赋值给运算符左边的变量。
// a、b、c两两进行按位与赋值
a &= b; // 10000 &= 1010 = 00000 = 0
console.log(a); // 0
b &= c; // 1010 &= 10010 = 00010 = 2
console.log(b); // 2
c &= a; // 10010 &= 00000 = 00000 = 0
console.log(c); // 0
// 负数进行按位与赋值
let d = 10;
d &= -5; // 1010 &= 11111011 = 1010 = 10
console.log(d); // 10
|&
(按位或赋值) 该组合运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行|
(或)操作。然后再将运算后的结果赋值给运算符左边的变量。
// 进行按位或赋值
a |= b; // 10000 |= 1010 = 11010 = 26
console.log(a); // 26
// 负数进行按位或赋值
let e = 10;
e |= -5; // 1010 | 11111011 = 11111111(补码) = -5
console.log(e); // -5
^=
(按位异或赋值) 该组合运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行^
(异或)操作。然后再将运算后的结果赋值给运算符左边的变量。
// 进行按位异或赋值
a ^= b; // 10000 ^= 1010 = 11010 = 26
console.log(a); // 26
// 负数进行按位异或赋值
let f = 10;
f ^= -5; // 1010 ^= 11111011 = 11110001(补码) = -15
console.log(f); // -15
<<=
(左移赋值) 该组合运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向左移动运算符右侧数字的位数。最后再将运算的结果赋值给运算符左侧的变量。
由于向左移动,左边超出32的位数将会被清除,右边会空出位,填充0
// 进行左移赋值
a <<= 1; // 10000 <<= 1 = 100000 = 32
console.log(a); // 32
// 负数进行左移赋值
let g = -5;
g <<= 1; // 11111011(补码) <<= 1 = 11110110(补码) = -10
console.log(g); // -10
>>=
(算术右移赋值) 该组合运算符表示该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。最后再将运算的结果赋值给运算符左侧的变量。
由于向右移动,右侧被移出的位会被丢弃,左侧空出的位会根据最左侧的符号位进行填充。因此负数进行算术右移的结果一定为负数,正数进行算术右移的结果一定为正数。
// 进行算术右移赋值
a >>= 1; // 10000 >>= 1 = 01000 = 8
console.log(a); // 8
// 负数进行算术右移赋值
let h = -5;
h >>= 1; // 11111011(补码) >>= 1 = 11111101(补码) = -3
console.log(h); // -3
>>>=
(无符号右移赋值) 该组合运算符表示该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。最后再将运算的结果赋值给运算符左侧的变量。
由于向右移动,右侧被移出的位会被丢弃,左侧空出的位填充0
。因此无论正数还是负数,进行无符号右移的结果一定为非负数。
// 进行无符号右移赋值
a >>>= 1; // 10000 >>>= 1 = 1000 = 8
console.log(a); // 8
// 负数进行无符号右移赋值
let i = -5;
i >>>= 1; // 11111011(补码) >>>= 1 = 0111111111111111111111111111101 = 2147483645
console.log(i); // 2147483645
位运算符
运算符