W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
二進制位運算符用于直接對二進制位進行計算,一共有7個。
|
,表示若兩個二進制位都為0
,則結(jié)果為0
,否則為1
。&
,表示若兩個二進制位都為1,則結(jié)果為1,否則為0。~
,表示對一個二進制位取反。^
,表示若兩個二進制位不相同,則結(jié)果為1,否則為0。<<
,詳見下文解釋。>>
,詳見下文解釋。>>>
,詳見下文解釋。這些位運算符直接處理每一個比特位(bit),所以是非常底層的運算,好處是速度極快,缺點是很不直觀,許多場合不能使用它們,否則會使代碼難以理解和查錯。
有一點需要特別注意,位運算符只對整數(shù)起作用,如果一個運算子不是整數(shù),會自動轉(zhuǎn)為整數(shù)后再執(zhí)行。另外,雖然在 JavaScript 內(nèi)部,數(shù)值都是以64位浮點數(shù)的形式儲存,但是做位運算的時候,是以32位帶符號的整數(shù)進行運算的,并且返回值也是一個32位帶符號的整數(shù)。
i = i | 0;
上面這行代碼的意思,就是將i
(不管是整數(shù)或小數(shù))轉(zhuǎn)為32位整數(shù)。
利用這個特性,可以寫出一個函數(shù),將任意數(shù)值轉(zhuǎn)為32位整數(shù)。
function toInt32(x) {
return x | 0;
}
上面這個函數(shù)將任意值與0
進行一次或運算,這個位運算會自動將一個值轉(zhuǎn)為32位整數(shù)。下面是這個函數(shù)的用法。
toInt32(1.001) // 1
toInt32(1.999) // 1
toInt32(1) // 1
toInt32(-1) // -1
toInt32(Math.pow(2, 32) + 1) // 1
toInt32(Math.pow(2, 32) - 1) // -1
上面代碼中,toInt32
可以將小數(shù)轉(zhuǎn)為整數(shù)。對于一般的整數(shù),返回值不會有任何變化。對于大于或等于2的32次方的整數(shù),大于32位的數(shù)位都會被舍去。
二進制或運算符(|
)逐位比較兩個運算子,兩個二進制位之中只要有一個為1
,就返回1
,否則返回0
。
0 | 3 // 3
上面代碼中,0
和3
的二進制形式分別是00
和11
,所以進行二進制或運算會得到11
(即3
)。
位運算只對整數(shù)有效,遇到小數(shù)時,會將小數(shù)部分舍去,只保留整數(shù)部分。所以,將一個小數(shù)與0
進行二進制或運算,等同于對該數(shù)去除小數(shù)部分,即取整數(shù)位。
2.9 | 0 // 2
-2.9 | 0 // -2
需要注意的是,這種取整方法不適用超過32位整數(shù)最大值2147483647
的數(shù)。
2147483649.4 | 0;
// -2147483647
二進制與運算符(&
)的規(guī)則是逐位比較兩個運算子,兩個二進制位之中只要有一個位為0
,就返回0
,否則返回1
。
0 & 3 // 0
上面代碼中,0(二進制00
)和3(二進制11
)進行二進制與運算會得到00
(即0
)。
二進制否運算符(~
)將每個二進制位都變?yōu)橄喾粗担?code>0變?yōu)?code>1,1
變?yōu)?code>0)。它的返回結(jié)果有時比較難理解,因為涉及到計算機內(nèi)部的數(shù)值表示機制。
~ 3 // -4
上面表達式對3
進行二進制否運算,得到-4
。之所以會有這樣的結(jié)果,是因為位運算時,JavaScript 內(nèi)部將所有的運算子都轉(zhuǎn)為32位的二進制整數(shù)再進行運算。
3
的32位整數(shù)形式是00000000000000000000000000000011
,二進制否運算以后得到11111111111111111111111111111100
。由于第一位(符號位)是1,所以這個數(shù)是一個負數(shù)。JavaScript 內(nèi)部采用補碼形式表示負數(shù),即需要將這個數(shù)減去1,再取一次反,然后加上負號,才能得到這個負數(shù)對應(yīng)的10進制值。這個數(shù)減去1等于11111111111111111111111111111011
,再取一次反得到00000000000000000000000000000100
,再加上負號就是-4
??紤]到這樣的過程比較麻煩,可以簡單記憶成,一個數(shù)與自身的取反值相加,等于-1。
~ -3 // 2
上面表達式可以這樣算,-3
的取反值等于-1
減去-3
,結(jié)果為2
。
對一個整數(shù)連續(xù)兩次二進制否運算,得到它自身。
~~3 // 3
所有的位運算都只對整數(shù)有效。二進制否運算遇到小數(shù)時,也會將小數(shù)部分舍去,只保留整數(shù)部分。所以,對一個小數(shù)連續(xù)進行兩次二進制否運算,能達到取整效果。
~~2.9 // 2
~~47.11 // 47
~~1.9999 // 1
~~3 // 3
使用二進制否運算取整,是所有取整方法中最快的一種。
對字符串進行二進制否運算,JavaScript 引擎會先調(diào)用Number
函數(shù),將字符串轉(zhuǎn)為數(shù)值。
// 相當(dāng)于~Number('011')
~'011' // -12
// 相當(dāng)于~Number('42 cats')
~'42 cats' // -1
// 相當(dāng)于~Number('0xcafebabe')
~'0xcafebabe' // 889275713
// 相當(dāng)于~Number('deadbeef')
~'deadbeef' // -1
Number
函數(shù)將字符串轉(zhuǎn)為數(shù)值的規(guī)則,參見《數(shù)據(jù)的類型轉(zhuǎn)換》一章。
對于其他類型的值,二進制否運算也是先用Number
轉(zhuǎn)為數(shù)值,然后再進行處理。
// 相當(dāng)于 ~Number([])
~[] // -1
// 相當(dāng)于 ~Number(NaN)
~NaN // -1
// 相當(dāng)于 ~Number(null)
~null // -1
異或運算(^
)在兩個二進制位不同時返回1
,相同時返回0
。
0 ^ 3 // 3
上面表達式中,0
(二進制00
)與3
(二進制11
)進行異或運算,它們每一個二進制位都不同,所以得到11
(即3
)。
“異或運算”有一個特殊運用,連續(xù)對兩個數(shù)a
和b
進行三次異或運算,a^=b; b^=a; a^=b;
,可以互換它們的值。這意味著,使用“異或運算”可以在不引入臨時變量的前提下,互換兩個變量的值。
var a = 10;
var b = 99;
a ^= b, b ^= a, a ^= b;
a // 99
b // 10
這是互換兩個變量的值的最快方法。
異或運算也可以用來取整。
12.9 ^ 0 // 12
左移運算符(<<
)表示將一個數(shù)的二進制值向左移動指定的位數(shù),尾部補0
,即乘以2
的指定次方。向左移動的時候,最高位的符號位是一起移動的。
// 4 的二進制形式為100,
// 左移一位為1000(即十進制的8)
// 相當(dāng)于乘以2的1次方
4 << 1
// 8
-4 << 1
// -8
上面代碼中,-4
左移一位得到-8
,是因為-4
的二進制形式是11111111111111111111111111111100
,左移一位后得到11111111111111111111111111111000
,該數(shù)轉(zhuǎn)為十進制(減去1后取反,再加上負號)即為-8
。
如果左移0位,就相當(dāng)于將該數(shù)值轉(zhuǎn)為32位整數(shù),等同于取整,對于正數(shù)和負數(shù)都有效。
13.5 << 0
// 13
-13.5 << 0
// -13
左移運算符用于二進制數(shù)值非常方便。
var color = {r: 186, g: 218, b: 85};
// RGB to HEX
// (1 << 24)的作用為保證結(jié)果是6位數(shù)
var rgb2hex = function(r, g, b) {
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
.toString(16) // 先轉(zhuǎn)成十六進制,然后返回字符串
.substr(1); // 去除字符串的最高位,返回后面六個字符串
}
rgb2hex(color.r, color.g, color.b)
// "#bada55"
上面代碼使用左移運算符,將顏色的 RGB 值轉(zhuǎn)為 HEX 值。
右移運算符(>>
)表示將一個數(shù)的二進制值向右移動指定的位數(shù)。如果是正數(shù),頭部全部補0
;如果是負數(shù),頭部全部補1
。右移運算符基本上相當(dāng)于除以2
的指定次方(最高位即符號位參與移動)。
4 >> 1
// 2
/*
// 因為4的二進制形式為 00000000000000000000000000000100,
// 右移一位得到 00000000000000000000000000000010,
// 即為十進制的2
*/
-4 >> 1
// -2
/*
// 因為-4的二進制形式為 11111111111111111111111111111100,
// 右移一位,頭部補1,得到 11111111111111111111111111111110,
// 即為十進制的-2
*/
右移運算可以模擬 2 的整除運算。
5 >> 1
// 2
// 相當(dāng)于 5 / 2 = 2
21 >> 2
// 5
// 相當(dāng)于 21 / 4 = 5
21 >> 3
// 2
// 相當(dāng)于 21 / 8 = 2
21 >> 4
// 1
// 相當(dāng)于 21 / 16 = 1
頭部補零的右移運算符(>>>
)與右移運算符(>>
)只有一個差別,就是一個數(shù)的二進制形式向右移動時,頭部一律補零,而不考慮符號位。所以,該運算總是得到正值。對于正數(shù),該運算的結(jié)果與右移運算符(>>
)完全一致,區(qū)別主要在于負數(shù)。
4 >>> 1
// 2
-4 >>> 1
// 2147483646
/*
// 因為-4的二進制形式為11111111111111111111111111111100,
// 帶符號位的右移一位,得到01111111111111111111111111111110,
// 即為十進制的2147483646。
*/
這個運算實際上將一個值轉(zhuǎn)為32位無符號整數(shù)。
查看一個負整數(shù)在計算機內(nèi)部的儲存形式,最快的方法就是使用這個運算符。
-1 >>> 0 // 4294967295
上面代碼表示,-1
作為32位整數(shù)時,內(nèi)部的儲存形式使用無符號整數(shù)格式解讀,值為 4294967295(即(2^32)-1
,等于11111111111111111111111111111111
)。
位運算符可以用作設(shè)置對象屬性的開關(guān)。
假定某個對象有四個開關(guān),每個開關(guān)都是一個變量。那么,可以設(shè)置一個四位的二進制數(shù),它的每個位對應(yīng)一個開關(guān)。
var FLAG_A = 1; // 0001
var FLAG_B = 2; // 0010
var FLAG_C = 4; // 0100
var FLAG_D = 8; // 1000
上面代碼設(shè)置 A、B、C、D 四個開關(guān),每個開關(guān)分別占有一個二進制位。
然后,就可以用二進制與運算,檢查當(dāng)前設(shè)置是否打開了指定開關(guān)。
var flags = 5; // 二進制的0101
if (flags & FLAG_C) {
// ...
}
// 0101 & 0100 => 0100 => true
上面代碼檢驗是否打開了開關(guān)C
。如果打開,會返回true
,否則返回false
。
現(xiàn)在假設(shè)需要打開A
、B
、D
三個開關(guān),我們可以構(gòu)造一個掩碼變量。
var mask = FLAG_A | FLAG_B | FLAG_D;
// 0001 | 0010 | 1000 => 1011
上面代碼對A
、B
、D
三個變量進行二進制或運算,得到掩碼值為二進制的1011
。
有了掩碼,二進制或運算可以確保打開指定的開關(guān)。
flags = flags | mask;
上面代碼中,計算后得到的flags
變量,代表三個開關(guān)的二進制位都打開了。
二進制與運算可以將當(dāng)前設(shè)置中凡是與開關(guān)設(shè)置不一樣的項,全部關(guān)閉。
flags = flags & mask;
異或運算可以切換(toggle)當(dāng)前設(shè)置,即第一次執(zhí)行可以得到當(dāng)前設(shè)置的相反值,再執(zhí)行一次又得到原來的值。
flags = flags ^ mask;
二進制否運算可以翻轉(zhuǎn)當(dāng)前設(shè)置,即原設(shè)置為0
,運算后變?yōu)?code>1;原設(shè)置為1
,運算后變?yōu)?code>0。
flags = ~flags;
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: