概述
按位操作符(Bitwise operators) 将其操作数(operands)当作32位的比特序列(由0和1组成),而不是十进制、十六进制或八进制数值。例如,十进制数9,用二进制表示则为1001。按位操作符操作数字的二进制形式,但是返回值依然是标准的JavaScript数值。
有符号 32 位整数
-
二进制编码
// 其中 0b 代表二进制 314 (base 10) = 0b00000000000000000000000100111010 (base 2)
-
反码
314 (base 10) = 0b00000000000000000000000100111010 (base 2) ~314 (base 10) = 0b11111111111111111111111011000101 (base 2)
-
补码
314 (base 10) = 0b00000000000000000000000100111010 (base 2) ~314 (base 10) = 0b11111111111111111111111011000101 (base 2) // -314 = 314 反码 + 1 -314 (base 10) = 0b11111111111111111111111011000110 (base 2)
符号位
补码保证了当一个数是正数时,其最左的比特位是0,当一个数是负数时,其最左的比特位是1。因此,最左边的比特位被称为符号位。-
特殊数字
0 (base 10) = 00000000000000000000000000000000 (base 2) -1 (base 10) = 11111111111111111111111111111111 (base 2)
按位逻辑操作符
从概念上讲,按位逻辑操作符按遵守下面规则:
- 操作数被转换成32位整数,用比特序列(0和1组成)表示。超过32位的数字会被丢弃。
- 第一个操作数的每个比特位与第二个操作数的相应比特位匹配:第一位对应第一位,第二位对应第二位,以此类推。
- 位运算符应用到每对比特位,结果是新的比特值。
& (按位与)
a | b | a & b |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 & 9 (base 10) = 00000000000000000000000000001000 (base 2) = 8 (base 10)
注意:
0 & x === 0
-1 & x === x
| (按位或)
a | b | a 或 b |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)
注意:
0 | x === x
-1 | x === -1
2.2341543 | 0 === 2
'adkfajdsf' | 0 === 0
'123456' | 0 === 123456
~ (按位非)
a | ~a |
---|---|
0 | 1 |
1 | 0 |
9 (base 10) = 00000000000000000000000000001001 (base 2)
--------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)
注意:
~x === -(x + 1)
~-1 === 0
^ (按位异或)
a | b | a ^ b |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)
注意:
0 ^ x === x
-1 ^ x === ~x
标志位与掩码
标志位
位运算经常被用来创建、处理以及读取标志位序列——一种类似二进制的变量。标志位序列可以节省内存
。
例如,有 4 个标志位:
- 标志位 A:我们有 ant
- 标志位 B:我们有 bat
- 标志位 C:我们有 cat
- 标志位 D:我们有 duck
标志位通过位序列 DCBA
来表示
- 当一个位被
置位 (set)
时,它的值为1
- 当一个位被
清除 (clear)
时,它的值为0
例如:一个变量 flags
的二进制值为 0101:
const flags = 0b0101;
这个值表示:
- 标志位 A 是 true (我们有 ant);
- 标志位 B 是 false (我们没有 bat);
- 标志位 C 是 true (我们有 cat);
- 标志位 D 是 false (我们没有 duck);
掩码
掩码 (bitmask) 是一个通过与 | 或来读取标志位的位序列。典型的定义每个标志位的原语掩码如下:
const flag_A = 0b0001;
const flag_B = 0b0010;
const flag_C = 0b0100;
const flag_D = 0b1000;
新的掩码可以在以上掩码上使用逻辑运算创建。
应用:
- 某个特定的位可以通过与掩码做
&
运算得到,通过与掩码的&
运算可以去掉无关的位,得到特定的位const flags = 0b0101; if (flags & flag_C) { // 0101 & 0100 => 0100 => true // do something }
- 通过与掩码做
|
运算设置标志位,掩码中为 1 的位可以设置对应的位// 掩码 1100 可用来设置位 C 和 D let flags = 0b0101; const mask = flag_C | flag_D // 0b0100 | 0b1000 => 0b1100 flags |= mask // 0b0101 | 0b1100 => 0b1101
- 通过与掩码做
&
运算清除标志位,掩码中为 0 的位可以设置对应的位// 掩码 1010 可以用来清除标志位 A 和 C let flags = 0b1101; const mask = ~(flag_A | flag_C) // ~(0b0001 | 0b0100) => 0b1010 flags &= mask // 0b1101 & 0b1010 => 0b1000
- 标志位可以使用
^
运算切换。所有值为 1 的位可以切换对应的位// 掩码 0110 可以用来切换标志位 B 和 C let flags = 0b1100; const mask = flag_B | flag_C // 0b0010 | 0b0100 => 0b0110 flags ^= mask // 0b1100 ^ 0b0110 => 0b1010
- 所有标志位可以通过非运算翻转
let flags = 0b1010; flags = ~flags // ~0b1010 => 0b0101
德摩根定律
~(a & b) === ~a | ~b
~(a | b) === ~a & ~b
实用技能
~ + indexOf
const str = 'rawr';
const searchFor = 'a';
// ~-1 === 0
if (~str.indexOf(searchFor)) {
// searchFor 包含在字符串中
} else {
// searchFor 不包含在字符串中
}
// (~str.indexOf(searchFor))的返回值
// r == -1
// a == -2
// w == -3
通过某个条件来切换一个值为0或者1
let whether = false;
let number = 1;
// whether === true => num === 0
// whether === false => num === 1
num = whether ^ 1;
// number 在 0 和 1 之间切换
number ^= 1;
判断奇偶
if(a & 1) {
// 奇数
} else {
// 偶数
}