## 概述

按位操作符（Bitwise operators） 将其操作数（operands）当作32位的比特序列（由0和1组成），而不是十进制、十六进制或八进制数值。例如，十进制数9，用二进制表示则为1001。按位操作符操作数字的二进制形式，但是返回值依然是标准的JavaScript数值。

下面的表格总结了JavaScript中的按位操作符：

运算符 | 用法 | 描述
--- | --- | ---
按位与（ AND） | `a & b` | 对于每一个比特位，只有两个操作数相应的比特位都是1时，结果才为1，否则为0。
按位或（OR） | `a | b` | 对于每一个比特位，当两个操作数相应的比特位至少有一个1时，结果为1，否则为0。
按位异或（XOR） | `a ^ b` | 对于每一个比特位，当两个操作数相应的比特位有且只有一个1时，结果为1，否则为0。
按位非（NOT） | `~ a` | 反转操作数的比特位，即0变成1，1变成0。
左移（L eft shift） | `a << b` | 将 `a` 的二进制形式向左移 `b` (< 32) 比特位，右边用0填充。
有符号右移 | `a >> b` | 将 a 的二进制表示向右移`b` (< 32) 位，丢弃被移出的位。
无符号右移 | `a >>> b` | 将 a 的二进制表示向右移`b` (< 32) 位，丢弃被移出的位，并使用 0 在左侧填充。

## 有符号32位整数

所有的按位操作符的操作数都会被转成补码（two's complement）形式的有符号32位整数。补码形式是指一个数的负对应值（negative counterpart）（如 5和-5）为数值的所有比特位反转后，再加1。反转比特位即该数值进行’非‘位运算，也即该数值的反码。例如下面为整数314的二进制编码：

    00000000000000000000000100111010

下面编码 `~314`，即 `314` 的反码：

    11111111111111111111111011000101

最后，下面编码 `-314`，即 `314` 的补码：

    11111111111111111111111011000110

补码保证了当一个数是正数时，其最左的比特位是0，当一个数是负数时，其最左的比特位是1。因此，最左边的比特位被称为符号位（sign bit）。

`0` 是所有比特数字0组成的整数。

    0 (base 10) = 00000000000000000000000000000000 (base 2)

`-1` 是所有比特数字1组成的整数。

    -1 (base 10) = 11111111111111111111111111111111 (base 2)

`-2147483648`（十六进制形式：`-0x80000000`）是除了最左边为1外，其他比特位都为0的整数。

    -2147483648 (base 10) = 10000000000000000000000000000000 (base 2)

`2147483647`（十六进制形式：`0x7fffffff`）是除了最左边为0外，其他比特位都为1的整数。

    2147483647 (base 10) = 01111111111111111111111111111111 (base 2)

数字`-2147483648` 和 `2147483647` 是32位有符号数字所能表示的最小和最大整数。

## 按位逻辑操作符

从概念上讲，按位逻辑操作符按遵守下面规则：

- 操作数被转换成32位整数，用比特序列（0和1组成）表示。超过32位的数字会被丢弃。
  例如, 以下具有32位以上的整数将转换为32位整数:

-     转换前: 11100110111110100000000000000110000000000001
      转换后:             10100000000000000110000000000001

- 第一个操作数的每个比特位与第二个操作数的相应比特位匹配：第一位对应第一位，第二位对应第二位，以此类推。
- 位运算符应用到每对比特位，结果是新的比特值。

### & (按位与)

对每对比特位执行与（AND）操作。只有 a 和 b 都是 1 时，a AND b 才是 1。与操作的真值表如下：

a | b | a AND 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)

将任一数值 x 与 0 执行按位与操作，其结果都为 0。将任一数值 x 与 -1 执行按位与操作，其结果都为 x。

### | (按位或)

对每一对比特位执行或（OR）操作。如果 a 或 b 为 1，则 `a` OR `b` 结果为 1。或操作的真值表：

a | b | a OR 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)

将任一数值 x 与 0 进行按位或操作，其结果都是 x。将任一数值 x 与 -1 进行按位或操作，其结果都为 -1。

补充一些例子：

    1 | 0 ;                       // 1

    1.1 | 0 ;                     // 1

    'asfdasfda' | 0 ;             // 0

    0 | 0 ;                       // 0

    (-1) | 0 ;                    // -1

    (-1.5646) | 0 ;               // -1

    [] | 0 ;                      // 0

    ({}) | 0 ;                    // 0

    "123456" | 0 ;            // 123456

    1.23E2 | 0;               // 123

    1.23E12 | 0;              // 1639353344

    -1.23E2 | 0;              // -123

    -1.23E12 | 0;             // -1639353344

### ^ (按位异或)

对每一对比特位执行异或（XOR）操作。当 a 和 b 不相同时，`a` XOR `b` 的结果为 1。异或操作真值表：

a | b | a XOR 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)

将任一数值 x 与 0 进行异或操作，其结果为 x。将任一数值 x 与 -1 进行异或操作，其结果为 ~x。

### ~ (按位非)

对每一个比特位执行非（NOT）操作。NOT `a` 结果为 a 的反转（即反码）。非操作的真值表：

a | NOT a
0 | 1
1 | 0

     9 (base 10) = 00000000000000000000000000001001 (base 2)
                   --------------------------------
    ~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)

对任一数值 x 进行按位非操作的结果为 -(x + 1)。例如，~5 结果为 -6。

与 indexOf 一起使用示例：

    var str = 'rawr';
    var searchFor = 'a';

    // 这是 if (-1*str.indexOf('a') <= 0) 条件判断的另一种方法
    if (~str.indexOf(searchFor)) {
      // searchFor 包含在字符串中
    } else {
      // searchFor 不包含在字符串中
    }

    // (~str.indexOf(searchFor))的返回值
    // r == -1
    // a == -2
    // w == -3

## 按位移动操作符

按位移动操作符有两个操作数：第一个是要被移动的数字，而第二个是要移动的长度。移动的方向根据操作符的不同而不同。

按位移动会先将操作数转换为大端字节序顺序(big-endian order)的32位整数,并返回与左操作数相同类型的结果。右操作数应小于 32位，否则只有最低 5 个字节会被使用。

    注：Big-Endian:高位字节排放在内存的低地址端，低位字节排放在内存的高地址端，
    又称为"高位编址"。
    Big-Endian是最直观的字节序：
    ①把内存地址从左到右按照由低到高的顺序写出；
    ②把值按照通常的高位到低位的顺序写出；
    ③两者对照，一个字节一个字节的填充进去。

### << (左移)

该操作符会将第一个操作数向左移动指定的位数。向左被移出的位被丢弃，右侧用 0 补充。

For example, `9 << 2` yields 36:

         9 (base 10): 00000000000000000000000000001001 (base 2)
                      --------------------------------
    9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)

在数字 x 上左移 y 比特得到 x * 2y.

### >> (有符号右移)

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃，拷贝最左侧的位以填充左侧。由于新的最左侧的位总是和以前相同，符号位没有被改变。所以被称作“符号传播”。

例如， `9 >> 2` 得到 2:

         9 (base 10): 00000000000000000000000000001001 (base 2)
                      --------------------------------
    9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

相比之下， `-9 >> 2` 得到 -3，因为符号被保留了。

         -9 (base 10): 11111111111111111111111111110111 (base 2)
                       --------------------------------
    -9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)

### >>> (无符号右移)

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃，左侧用0填充。因为符号位变成了 0，所以结果总是非负的。（译注：即便右移 0 个比特，结果也是非负的。）

对于非负数，有符号右移和无符号右移总是返回相同的结果。例如， `9 >>> 2` 得到 2 和 `9 >> 2` 相同：

          9 (base 10): 00000000000000000000000000001001 (base 2)
                       --------------------------------
    9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

但是对于负数却不尽相同。 `-9 >>> 2` 产生 1073741821 这和 `-9 >> 2` 不同：

          -9 (base 10): 11111111111111111111111111110111 (base 2)
                        --------------------------------
    -9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)

## 示例

### 例子：标志位与掩码

位运算经常被用来创建、处理以及读取标志位序列——一种类似二进制的变量。虽然可以使用变量代替标志位序列，但是这样可以节省内存（1/32）。

例如，有 4 个标志位：

- 标志位 A：我们有 ant
- 标志位 B：我们有 bat
- 标志位 C：我们有 cat
- 标志位 D：我们有 duck

标志位通过位序列 DCBA 来表示。当一个位被置位 (set) 时，它的值为 1 。当被清除 (clear) 时，它的值为 0 。例如一个变量 `flags` 的二进制值为 0101：

    var flags = 5;   // 二进制 0101

这个值表示：

- 标志位 A 是 true （我们有 ant）；
- 标志位 B 是 false （我们没有 bat）；
- 标志位 C 是 true （我们有 cat）；
- 标志位 D 是 false （我们没有 duck）；

因为位运算是 32 位的， 0101 实际上是 00000000000000000000000000000101。因为前面多余的 0 没有任何意义，所以他们可以被忽略。

掩码 (bitmask) 是一个通过与/或来读取标志位的位序列。典型的定义每个标志位的原语掩码如下：

    var FLAG_A = 1; // 0001
    var FLAG_B = 2; // 0010
    var FLAG_C = 4; // 0100
    var FLAG_D = 8; // 1000

新的掩码可以在以上掩码上使用逻辑运算创建。例如，掩码 1011 可以通过 FLAG_A、FLAG_B 和 FLAG_D 逻辑或得到：

    var mask = FLAG_A | FLAG_B | FLAG_D; // 0001 | 0010 | 1000 => 1011

某个特定的位可以通过与掩码做逻辑与运算得到，通过与掩码的与运算可以去掉无关的位，得到特定的位。例如，掩码 0100 可以用来检查标志位 C 是否被置位：

    // 如果我们有 cat
    if (flags & FLAG_C) { // 0101 & 0100 => 0100 => true
       // do stuff
    }

一个有多个位被置位的掩码表达任一/或者的含义。例如，以下两个表达是等价的：

    // 如果我们有 bat 或者 cat 至少一个
    // (0101 & 0010) || (0101 & 0100) => 0000 || 0100 => true
    if ((flags & FLAG_B) || (flags & FLAG_C)) {
       // do stuff
    }

    // 如果我们有 bat 或者 cat 至少一个
    var mask = FLAG_B | FLAG_C; // 0010 | 0100 => 0110
    if (flags & mask) { // 0101 & 0110 => 0100 => true
       // do stuff
    }

可以通过与掩码做或运算设置标志位，掩码中为 1 的位可以设置对应的位。例如掩码 1100 可用来设置位 C 和 D：

    // 我们有 cat 和 duck
    var mask = FLAG_C | FLAG_D; // 0100 | 1000 => 1100
    flags |= mask;   // 0101 | 1100 => 1101

可以通过与掩码做与运算清除标志位，掩码中为 0 的位可以设置对应的位。掩码可以通过对原语掩码做非运算得到。例如，掩码 1010 可以用来清除标志位 A 和 C ：

    // 我们没有 ant 也没有 cat
    var mask = ~(FLAG_A | FLAG_C); // ~0101 => 1010
    flags &= mask;   // 1101 & 1010 => 1000

如上的掩码同样可以通过 `~FLAG_A & ~FLAG_C` 得到（德摩根定律）：

    // 我们没有 ant 也没有 cat
    var mask = ~FLAG_A & ~FLAG_C;
    flags &= mask;   // 1101 & 1010 => 1000

标志位可以使用异或运算切换。所有值为 1 的为可以切换对应的位。例如，掩码 0110 可以用来切换标志位 B 和 C：

    // 如果我们以前没有 bat ，那么我们现在有 bat
    // 但是如果我们已经有了一个，那么现在没有了
    // 对 cat 也是相同的情况
    var mask = FLAG_B | FLAG_C;
    flags = flags ^ mask;   // 1100 ^ 0110 => 1010

最后，所有标志位可以通过非运算翻转:

    // entering parallel universe...
    flags = ~flags;    // ~1010 => 0101

### 转换片段

将一个二进制数的 `String` 转换为十进制的 `Number`:

    var sBinString = "1011";
    var nMyNumber = parseInt(sBinString, 2);
    alert(nMyNumber); // 打印 11

将一个十进制的 `Number` 转换为二进制数的 `String`:

    var nMyNumber = 11;
    var sBinString = nMyNumber.toString(2);
    alert(sBinString); // 打印 1011

### 自动化掩码创建

如果你需要从一系列的 `Boolean` 值创建一个掩码，你可以：

    function createMask () {
      var nMask = 0, nFlag = 0, nLen = arguments.length > 32 ? 32 : arguments.length;
      for (nFlag; nFlag < nLen; nMask |= arguments[nFlag] << nFlag++);
      return nMask;
    }
    var mask1 = createMask(true, true, false, true); // 11, i.e.: 1011
    var mask2 = createMask(false, false, true); // 4, i.e.: 0100
    var mask3 = createMask(true); // 1, i.e.: 0001
    // etc.

    alert(mask1); // 打印 11

### 逆算法：从掩码得到布尔数组

如果你希望从掩码得到得到 `Boolean` `Array` ：

    function arrayFromMask (nMask) {
      // nMask 必须介于 -2147483648 和 2147483647 之间
      if (nMask > 0x7fffffff || nMask < -0x80000000) {
        throw new TypeError("arrayFromMask - out of range");
      }
      for (var nShifted = nMask, aFromMask = []; nShifted;
           aFromMask.push(Boolean(nShifted & 1)), nShifted >>>= 1);
      return aFromMask;
    }

    var array1 = arrayFromMask(11);
    var array2 = arrayFromMask(4);
    var array3 = arrayFromMask(1);

    alert("[" + array1.join(", ") + "]");
    // 打印 "[true, true, false, true]", i.e.: 11, i.e.: 1011

你可以同时测试以上两个算法……

    var nTest = 19; // our custom mask
    var nResult = createMask.apply(this, arrayFromMask(nTest));

    alert(nResult); // 19

仅仅由于教学目的 （因为有 `Number.toString(2)` 方法），我们展示如何修改 arrayFromMask 算法通过 Number 返回二进制的 String，而非 Boolean Array：

    function createBinaryString (nMask) {
      // nMask must be between -2147483648 and 2147483647
      for (var nFlag = 0, nShifted = nMask, sMask = ""; nFlag < 32;
           nFlag++, sMask += String(nShifted >>> 31), nShifted <<= 1);
      return sMask;
    }

    var string1 = createBinaryString(11);
    var string2 = createBinaryString(4);
    var string3 = createBinaryString(1);

    alert(string1);
    // 打印 00000000000000000000000000001011, i.e. 11

## 规范

Specification | Status | Comment
--- | --- | ---
ECMAScript 1st Edition. | Standard | Initial definition.
ECMAScript 5.1 (ECMA-262) Bitwise NOT operator ECMAScript 5.1 (ECMA-262) Bitwise shift operators ECMAScript 5.1 (ECMA-262) Binary bitwise operators | Standard |
ECMAScript 2015 (6th Edition, ECMA-262) Bitwise NOT operator ECMAScript 2015 (6th Edition, ECMA-262) Bitwise shift operators ECMAScript 2015 (6th Edition, ECMA-262) Binary bitwise operators | Standard |

## 浏览器兼容性

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

- Desktop
- Mobile

Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari
--- | --- | --- | --- | --- | ---
Bitwise NOT (`~`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Bitwise AND (`&`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Bitwise OR (`|`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Bitwise XOR (`^`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Left shift (`<<`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Right shift (`>>`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Unsigned right shift (`>>>`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)

Feature | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile
--- | --- | --- | --- | --- | --- | ---
Bitwise NOT (`~`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Bitwise AND (`&`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Bitwise OR (`|`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Bitwise XOR (`^`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Left shift (`<<`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Right shift (`>>`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)
Unsigned right shift (`>>>`) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes)

## 相关链接

- Logical operators

- js ^ & Bitwise Operators