一、概念
位運算在數字底層(表示數字的32個數位)進行運算的。由於位運算是低級的運算操作,所以速度往往也是最快的,但是它很不直觀,許多場合不能夠使用。大多數語言都提供了按位運算符,恰當的使用按位運算符有時候會取得很好的效果。
位運算只對整數起作用,如果一個運算不是整數,會自動轉為整數后再運行。雖然在Javascript內部,數值都是以64位浮點數的形式儲存,但是做位運算的時候,是以32位帶符號的整數進行運算的,並且返回值也是一個32位帶符號的整數。
這種位轉換使得在對特殊的NaN和infinity值應用位操作時,這兩個值會被當成0來處理。如果是對非數值應用位操作符時,會先使用Number()方法將值轉換成數值再應用位操作,得到的結果是一個數值。
二進制:ECMAScript整數有兩種類型,即有符號整數(允許用正數和負數)和無符號整數(只允許用正數)。在 ECMAScript 中,所有整數字面量默認都是有符號整數。
有符號整數使用 31 位表示整數的數值,用第 32 位表示整數的符號,0 表示正數,1 表示負數。數值范圍從 -2147483648 到 2147483647。表示符號的位叫做符號位,符號位的值決定了其它位數值的格式。其中,正數以純二進制格式存儲,31位中的每一位都表示2的冪,第一位(叫做位0)表示2的1次以此類推,沒有用到的位以0填充,可忽略不計。
如:數10的表示法
10的二進制是‘1010’.它只用了前4位,這4位是數字的有效位。其它的數位可以忽略不計。
console.log((10).toString(2)); //1010
反推的話:
負數可以存儲為二進制代碼,不過采用的形式是二進制補碼,計算數字二進制補碼的步驟如下:
1、確定數字的非負版本的二進制表示
2、求得二進制反碼后要把0替換為1,1替換為0
3、在二進制反碼上加1
如:確定-10的二進制補碼
1、10的二進制表示如下:0000 0000 0000 0000 0000 0000 0000 1010
2、把0替換1,1替換0: 1111 1111 1111 1111 1111 1111 1111 0101
3、在二進制反碼上加1
所以-10的二進制表示是1111 1111 1111 1111 1111 1111 0110.需要注意的一點是在處理有符號的整數時不能訪問31位。但是,把負整數轉換成二進制字符串后,ECMAScript並不以二進制補碼的形式顯示,而是用數字絕對值的標准二進制代碼前面加負號的形式輸出,這是為了避免訪問位31位,為了方便 。
例子如下:console.log((-10).toString(2)); //-1010
位運算符可以進行7種運算,包括位非(NOT),按位與(AND),按位或(OR),按位異或(XOR),左移,有符號右移,無符號右移。
二、具體分析
1、按位非(NOT)
按位非操作符由一個波浪線(~)表示,操作它可以返回數值的反碼,其本質是操作數的負值減1.對一個整數兩次按位非得到它的自身, 對於小數兩次按位非可以得到取整的效果。
<script>
let n = 9; let i = 9.9; let m = ~n; console.log(m); //-10
console.log(~~n);//9
console.log(~~i);//9
</script>
2、按位與(AND)
按位與操作符由一個和符號(&)表示,它有兩個操作數。直接對數字的二進制形式進行運算。它把每個數字中的數位對齊,然后用下面的規則對同一位置上的兩個數位進行AND運算。按位與操作只有在兩個數值的對應位都是1時才返回1,任何一位是0結果都是為0.
<script>
let n = 10; let m = 15; console.log(n & m); //10
console.log(n.toString(2), m.toString(2)); //1010 1111
</script>
分析如下:
3、按位或(OR)
按位或操作符由一個豎線符號(|)表示,同樣也有兩個操作符,按位或操作按照下表來進行。按位或操作在有一個位是1的情況下就返回1,而只有在兩個位數都是0的情況下才返回0.一個整數與0按位或運算可以得到它本身,一個小數與0按位或者運算可以得到取整的效果。
<script>
let n = 10; let m = 15; console.log(n | m); //15
console.log(3.9 | 0); //3
console.log(4.1 | 0); //4
console.log(n.toString(2), m.toString(2)); //1010 1111
</script>
分析如下:
4、按位異或(XOR)
它由一個插入符號(^)表示,也需要兩個操作數,以下是真值表。按位異或的兩個數值相同時返回0,不同時返回1。一個整數與0按位異或可以保持自身,一個小數與0按位異或可以取整。
<script>
let n = 10; let m = 15; console.log(n ^ m); //5
console.log(n.toString(2), m.toString(2)); //1010 1111
</script>
分析如下:
^有一個特殊運用,連續對兩個數a和b進行三次異或運算,a^=b,b^=a,a^=b可以互換它們的值。這意味着,使用^可以在不引入臨時變量的前提下,互換兩個變量的值。
<script>
let a = 10, b = 11; a ^= b, b ^= a, a ^= b; console.log(a, b); //11 10
</script>
5、左移
左移操作符由兩上小於號(<<)表示,這個操作符會把數值的所有位向左移動指定的位數。
如:將數值3(二進制為11)向左移動5位,結果就是96
<script>
let n=3; let m=n<<5; console.log(n.toString(2)); //11
console.log(m); //96
</script>
分析如下:
6、有符號右移
由兩個大於號(>>)表示,將數值向右移動,但保留符號位。在符號的右移操作與左移操作正好相反,如果96向右移動5位變成3。
<script>
let n = 96; console.log(n >> 5);//3
</script>
7、無符號右移
它是由三個大於號來表示(>>>),這個操作符會將數值的所有32位都向右移動,對於正數來說,無符號右移的結果和有符號右移的結果相同。但對於負數來說,無符號右移是以0來填充空位而不是像有符號那樣以符號位的值來填充空位。還有一點是,無符號右移操作符會把負數的二進制碼當成正數的二進制碼且由於負數以其絕對值的二進制補碼形式表示,所以會導致無符號右移后的結果非常的大。
如:-96無符號右移5
<script>
let n = -96; console.log(n >>> 5);//134217725
</script>
分析:
三、常見的運用
利用<<來實現乘法運算;利用>>實現除法運算;利用^來實現值互換;小數取整
<script> console.log(4 << 1);//8
console.log(4 << 2);//16
console.log(4 << 3);//32
console.log(4 << 4);//64
console.log(4 << 5);//128
console.log(4 << 6);//256
console.log(5 >> 3);//0
console.log(14 >> 2);//3
console.log(225 >> 5);//7
let a = 20, b = 4; a ^= b, b ^= a, a ^= b; console.log(a, b);//4 20
console.log(~~9.9);//9
console.log(9.8 | 0);//9
console.log(9.7 ^ 0);//9
console.log(9.6 << 0);//9
console.log(9.3 >> 0);//9
</script>