二进制位运算符
二进制或运算符
二进制或运算符(|)逐位比较两个运算子,两个二进制位之中只要有一个为1,就返回1,否则返回0。
1 | 0 | 3 // 3 |
上面代码中,0和3的二进制形式分别是00和11,所以进行二进制或运算会得到11(即3)。
位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分。所以,将一个小数与0进行二进制或运算,等同于对该数去除小数部分,即取整数位。
1 | 2.9 | 0 // 2 |
需要注意的是,这种取整方法不适用超过32位整数最大值2147483647的数。
1 | 2147483649.4 | 0; |
二进制与运算符
二进制与运算符(&)的规则是逐位比较两个运算子,两个二进制位之中只要有一个位为0,就返回0,否则返回1。
1 | 0 & 3 // 0 |
上面代码中,0(二进制00)和3(二进制11)进行二进制与运算会得到00(即0)。
二进制否运算符
二进制否运算符(~)将每个二进制位都变为相反值(0变为1,1变为0)。它的返回结果有时比较难理解,因为涉及到计算机内部的数值表示机制。
1 | ~ 3 // -4 |
上面表达式对3进行二进制否运算,得到-4。之所以会有这样的结果,是因为位运算时,JavaScript 内部将所有的运算子都转为32位的二进制整数再进行运算。
3的32位整数形式是00000000000000000000000000000011,二进制否运算以后得到11111111111111111111111111111100。由于第一位(符号位)是1,所以这个数是一个负数。JavaScript 内部采用补码形式表示负数,即需要将这个数减去1,再取一次反,然后加上负号,才能得到这个负数对应的10进制值。这个数减去1等于11111111111111111111111111111011,再取一次反得到00000000000000000000000000000100,再加上负号就是-4。考虑到这样的过程比较麻烦,可以简单记忆成,一个数与自身的取反值相加,等于**-1**。
1 | ~ -3 // 2 |
上面表达式可以这样算,-3的取反值等于-1减去-3,结果为2。
对一个整数连续两次二进制否运算,得到它自身。
1 | ~~3 // 3 |
所有的位运算都只对整数有效。二进制否运算遇到小数时,也会将小数部分舍去,只保留整数部分。所以,对一个小数连续进行两次二进制否运算,能达到取整效果。
1 | ~~2.9 // 2 |
使用二进制否运算取整,是所有取整方法中最快的一种。
对字符串进行二进制否运算,JavaScript 引擎会先调用Number函数,将字符串转为数值。
1 | // 相当于~Number('011') |
Number函数将字符串转为数值的规则,参见《数据的类型转换》一章。
对于其他类型的值,二进制否运算也是先用Number转为数值,然后再进行处理。
1 | // 相当于 ~Number([]) |
异或运算符(没整懂。。。)
异或运算(^)在两个二进制位不同时返回1,相同时返回0。
1 | 0 ^ 3 // 3 |
上面表达式中,0(二进制00)与3(二进制11)进行异或运算,它们每一个二进制位都不同,所以得到11(即3)。
“异或运算”有一个特殊运用,连续对两个数a和b进行三次异或运算,a^=b; b^=a; a^=b;,可以互换它们的值。这意味着,使用“异或运算”可以在不引入临时变量的前提下,互换两个变量的值。
1 | var a = 10; |
这是互换两个变量的值的最快方法。
异或运算也可以用来取整。
1 | 12.9 ^ 0 // 12 |
左移运算符
左移运算符(<<)表示将一个数的二进制值向左移动指定的位数,尾部补0,即乘以2的指定次方。向左移动的时候,最高位的符号位是一起移动的。
1 | // 4 的二进制形式为100, |
上面代码中,-4左移一位得到-8,是因为-4的二进制形式是11111111111111111111111111111100,左移一位后得到11111111111111111111111111111000,该数转为十进制(减去1后取反,再加上负号)即为-8。
如果左移0位,就相当于将该数值转为32位整数,等同于取整,对于正数和负数都有效。
1 | 13.5 << 0 |
左移运算符用于二进制数值非常方便。
1 | var color = {r: 186, g: 218, b: 85}; |
上面代码使用左移运算符,将颜色的 RGB 值转为 HEX 值。
右移运算符
右移运算符(>>)表示将一个数的二进制值向右移动指定的位数。如果是正数,头部全部补0;如果是负数,头部全部补1。右移运算符基本上相当于除以2的指定次方(最高位即符号位参与移动)。
1 | 4 >> 1 |
右移运算可以模拟 2 的整除运算。
1 | 5 >> 1 |
##头部补零的右移运算符
头部补零的右移运算符(>>>)与右移运算符(>>)只有一个差别,就是一个数的二进制形式向右移动时,头部一律补零,而不考虑符号位。所以,该运算总是得到正值。对于正数,该运算的结果与右移运算符(>>)完全一致,区别主要在于负数。
1 | 4 >>> 1 |
这个运算实际上将一个值转为32位无符号整数。
查看一个负整数在计算机内部的储存形式,最快的方法就是使用这个运算符。
1 | -1 >>> 0 // 4294967295 |
上面代码表示,-1作为32位整数时,内部的储存形式使用无符号整数格式解读,值为 4294967295(即(2^32)-1,等于11111111111111111111111111111111)。
参考链接
- Michal Budzynski, JavaScript: The less known parts. Bitwise Operators
- Axel Rauschmayer, Basic JavaScript for the impatient programmer
- Mozilla Developer Network, Bitwise Operators