正在翻譯中。

位元運算子將運算元視為一段 32 位元長的 0 和 1 序列,而不是十進位、十六進位或八進位的 Numbers。 舉例來說,十進位的 9 可以用二進位表示為 1001。位元運算子對這樣的二進位表示法進行運算,然後回傳標準 JavaScript 數值。

下表總結了 JavaScript 的位元運算子:

Operator Usage Description
位元 AND a & b 當兩運算元的該位置皆為 1 時,回傳值的該位置為 1
位元 OR a | b 當兩運算元的該位置有一者為 1 時,回傳值的該位置為 1
位元 XOR a ^ b 當兩運算元的該位置恰好一者為 1 時,回傳值的該位置為 1
位元 NOT ~ a 將運算元的所有位元反轉。
左移 a << b 將 a 的二進位表示法左移 b (< 32) 位元,右側補 0
保持符號右移 a >> b 將 a 的二進位表示法右移 b (< 32) 位元,拋棄被移出的位元。
填零右移 a >>> b   將 a 的二進位表示法右移 b (< 32) 位元,拋棄被移出的位元,並於右側補 0

帶號的 32位元整數

所有位元運算子的運算元皆會被轉換成二補數系統下的帶號32位元整數。二補數系統意味著一個整數的加法反元素(例如 5和 -5)是該整數的所有位元反轉(位元 NOT,也就是該數的一補數) 再加一。舉例來說,下面的序列代表著整數 314:

00000000000000000000000100111010

下面的序列代表 ~314,也就是 314 的一補數:

11111111111111111111111011000101

接著,下面代表著 -314,也就是 314 的二補數:

11111111111111111111111011000110

二補數系統確保了正值時最左邊的位元為 0,反之則為 1。因此,最左邊的位元被稱作符號位。

整數 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)

整數 -2147483648 (十六進位: -0x7fffffff) 除了第一位為 0,其餘皆由位元 1組成。

2147483647 (base 10) = 01111111111111111111111111111111 (base 2)

整數 -2147483648 和 2147483647 分別為帶號32位元整數所能表示的最小值和最大值。

位元邏輯運算子

大致上,位元邏輯運算子的運作如下︰

  • 運算元會被轉換成 32位元的整數,並被表達為一系列的位元 (0 和 1)。多於 32位元的數值在轉換中其超出第32位元的部分會被捨棄。下面的多於32位元整數在被轉換時:
    Before: 11100110111110100000000000000110000000000001
    After:              10100000000000000110000000000001
  • 兩個運算元中的位元會根據其位置兩兩一組:第一個跟第一個、第二個跟第二個 ...
  • 運算子會作用在每一組位元上,運算完成後再重新組合起來得到回傳值。

& (位元 AND)

對每一組位元執行 AND 運算。a AND b 只在 a 和 b 同時為 1 時得到 1。AND運算的真值表如下:

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 做位元 AND 皆會得到 0。將任何數 x 和 -1 做位元 AND 皆會得到 x

| (位元 OR)

對每一組位元執行 OR 運算。a OR b 在 a 和 b 有一者為 1 時得到 1。OR運算的真值表如下:

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 做位元 OR 皆會得到 x。將任何數 x 和 -1 做位元 OR 皆會得到 -1

^ (位元 XOR)

對每一組位元執行 XOR 運算。a XOR b 只在 a 和 b 恰一者為 1 時得到 1。XOR運算的真值表如下:

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 做位元 AND 皆會得到 x。將任何數 x 和 -1 做位元 AND 皆會得到 ~x

~ (位元 NOT)

對每一個位元執行 NOT 運算。NOT a 會得到 a 的反轉值(也就是一補數)。NOT運算的真值表如下:

a NOT a
0 1
1 0
 9 (base 10) = 00000000000000000000000000001001 (base 2)
               --------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)

將任何數 x 做位元 NOT 皆會得到 -(x + 1)。舉例來說,~-5 會得到 4

值得注意的是,因為使用 32位元表示法表示數值 ~-1 和 ~4294967295 (232-1) 皆會得到 0

位元位移運算子

位移運算子需要兩個運算元:第一個是要被位移的值,第二個是位元位移量。位移的方向取決於使用的運算子。

位移運算子將運算元轉換成 32位元的大端序整數並回傳一個與左運算元相同類別的值。右運算元應不大於32,如果超過的話,將只會使用後 5個位元。

<< (左移)

將第一個運算元向左位移指定的量。被移出的位元會被拋棄,並從右側補零。

例如,9 << 2 會得到 36:

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

將任意值 x 左移 y 位元會得到 x * 2 ** y

>> (保持符號右移)

將第一個運算元向右位移指定的量。被移出的位元會被拋棄,並從左側補進和原本最左端相同的位元值。因為新的最左端位元和原本的最左端位元是一樣的,符號位(最左端位元)並不會改變。「保持符號」之名便是因此。

例如,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,所以結果永遠都是正值。

對非負的數來說,填零右移會得到和保持符號右移一樣的結果。例如,9 >>> 2 和 9 >> 2 一樣,皆會得到 2:

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

然而對負值來說並不是這麼一回事。例如,-9 >>> 2 會得到 1073741821,跟 -9 >> 2 (得到 -3)的結果是不一樣的:

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

範例

旗標(flags) 和遮罩 (bitmasks)

位元運算子常被用於生成、修改、和讀取旗標序列,就像是二進制的變數一般。雖然也可以使用普通變數,但使用二進制的旗標序列大大的減少了所需空間 (32 倍)。

假設有 4個旗標:

  • 旗標 A:我們有螞蟻問題
  • 旗標 B:我們擁有一隻蝙蝠
  • 旗標 C:我們擁有一隻貓
  • 旗標 D:我們擁有一隻鴨子

這些旗標倍表達成一個位元序列:DCBA。當一個旗標被立起 (set)時,其值為1。當一個旗標被放下 (clear),其值為0。假設有一變數 flags 的二進位值為 0101:

var flags = 5;   // 二進位 0101

這個值表示:

  • 旗標 A為真 (我們有螞蟻問題);
  • 旗標 B為假 (我們並未擁有一隻蝙蝠);
  • 旗標 C為真 (我們擁有一隻貓);
  • 旗標 D為假 (我們並未擁有一隻鴨子);

因為位元運算子進行的是 32位元操作,0101 實際上是 00000000000000000000000000000101,但前導的 0可被忽略因為他們沒有實際上的意義。

位元遮罩則為一個可以修改且(或)讀取旗標序列的位元序列。通常為每個單獨旗標為真的「初始」值:

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進行 OR運算獲得:

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

Individual flag values can be extracted by ANDing them with a bitmask, where each bit with the value of one will "extract" the corresponding flag. The bitmask masks out the non-relevant flags by ANDing with zeroes (hence the term "bitmask"). For example, the bitmask 0100 can be used to see if flag C is set:

// if we own a cat
if (flags & FLAG_C) { // 0101 & 0100 => 0100 => true
   // do stuff
}

A bitmask with multiple set flags acts like an "either/or". For example, the following two are equivalent:

// if we own a bat or we own a cat
// (0101 & 0010) || (0101 & 0100) => 0000 || 0100 => true
if ((flags & FLAG_B) || (flags & FLAG_C)) {
   // do stuff
}
// if we own a bat or cat
var mask = FLAG_B | FLAG_C; // 0010 | 0100 => 0110
if (flags & mask) { // 0101 & 0110 => 0100 => true
   // do stuff
}

Flags can be set by ORing them with a bitmask, where each bit with the value one will set the corresponding flag, if that flag isn't already set. For example, the bitmask 1100 can be used to set flags C and D:

// yes, we own a cat and a duck
var mask = FLAG_C | FLAG_D; // 0100 | 1000 => 1100
flags |= mask;   // 0101 | 1100 => 1101

Flags can be cleared by ANDing them with a bitmask, where each bit with the value zero will clear the corresponding flag, if it isn't already cleared. This bitmask can be created by NOTing primitive bitmasks. For example, the bitmask 1010 can be used to clear flags A and C:

// no, we don't have an ant problem or own a cat
var mask = ~(FLAG_A | FLAG_C); // ~0101 => 1010
flags &= mask;   // 1101 & 1010 => 1000

The mask could also have been created with ~FLAG_A & ~FLAG_C (De Morgan's law):

// no, we don't have an ant problem, and we don't own a cat
var mask = ~FLAG_A & ~FLAG_C;
flags &= mask;   // 1101 & 1010 => 1000

Flags can be toggled by XORing them with a bitmask, where each bit with the value one will toggle the corresponding flag. For example, the bitmask 0110 can be used to toggle flags B and C:

// if we didn't have a bat, we have one now, 
// and if we did have one, bye-bye bat
// same thing for cats
var mask = FLAG_B | FLAG_C;
flags = flags ^ mask;   // 1100 ^ 0110 => 1010

Finally, the flags can all be flipped with the NOT operator:

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

Conversion snippets

Convert a binary String to a decimal Number:

var sBinString = '1011';
var nMyNumber = parseInt(sBinString, 2);
alert(nMyNumber); // prints 11, i.e. 1011

Convert a decimal Number to a binary String:

var nMyNumber = 11;
var sBinString = nMyNumber.toString(2);
alert(sBinString); // prints 1011, i.e. 11

Automate Mask Creation

You can create multiple masks from a set of Boolean values, like this:

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); // prints 11, i.e.: 1011

Reverse algorithm: an array of booleans from a mask

If you want to create an Array of Booleans from a mask you can use this code:

function arrayFromMask(nMask) {
  // nMask must be between -2147483648 and 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(', ') + ']');
// prints "[true, true, false, true]", i.e.: 11, i.e.: 1011

You can test both algorithms at the same time…

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

alert(nResult); // 19

For the didactic purpose only (since there is the Number.toString(2) method), we show how it is possible to modify the arrayFromMask algorithm in order to create a String containing the binary representation of a Number, rather than an Array of Booleans:

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);
// prints 00000000000000000000000000001011, i.e. 11

規範

Specification Status Comment
ECMAScript 1st Edition (ECMA-262) Standard Initial definition.
ECMAScript 5.1 (ECMA-262) Standard Defined in several sections of the specification: Bitwise NOT operator, Bitwise shift operators, Binary bitwise operators
ECMAScript 2015 (6th Edition, ECMA-262) Standard Defined in several sections of the specification: Bitwise NOT operator, Bitwise shift operators, Binary bitwise operators
ECMAScript Latest Draft (ECMA-262) Draft Defined in several sections of the specification: Bitwise NOT operator, Bitwise shift operators, Binary bitwise operators

瀏覽器相容性

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidEdge MobileFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
Bitwise AND (a & b)Chrome Full support YesEdge Full support YesFirefox Full support 1IE Full support YesOpera Full support YesSafari Full support YesWebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes
Bitwise left shift (a << b)Chrome Full support YesEdge Full support YesFirefox Full support 1IE Full support YesOpera Full support YesSafari Full support YesWebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes
Bitwise NOT (~a)Chrome Full support YesEdge Full support YesFirefox Full support 1IE Full support YesOpera Full support YesSafari Full support YesWebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes
Bitwise OR (a | b)Chrome Full support YesEdge Full support YesFirefox Full support 1IE Full support YesOpera Full support YesSafari Full support YesWebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes
Bitwise right shift (a >> b)Chrome Full support YesEdge Full support YesFirefox Full support 1IE Full support YesOpera Full support YesSafari Full support YesWebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes
Bitwise unsigned right shift (a >>> b)Chrome Full support YesEdge Full support YesFirefox Full support 1IE Full support YesOpera Full support YesSafari Full support YesWebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes
Bitwise XOR (a ^ b)Chrome Full support YesEdge Full support YesFirefox Full support 1IE Full support YesOpera Full support YesSafari Full support YesWebView Android Full support YesChrome Android Full support YesEdge Mobile Full support YesFirefox Android Full support 4Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support Yes

Legend

Full support  
Full support

另見

文件標籤與貢獻者

此頁面的貢獻者: hmysjiang
最近更新: hmysjiang,