什么是位运算?
从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算,即将符号位共同参与运算的运算。
二进制表示
JavaScript 将数字存储为 64 位浮点数,但所有按位运算都以 32 位二进制数执行。在执行位运算之前,JavaScript 将数字转换为 32 位有符号整数。执行按位操作后,结果将转换回 64 位 JavaScript 数。位运算只对整数起作用,如果一个运算子不是整数,会自动转为整数后再运行。
在 ECMAScript 中,所有整数字面量默认都是有符号整数,有符号整数使用 31 位表示整数的数值,用第 32 位表示整数的符号,0 表示正数,1 表示负数。数值范围从 -2147483648 到 2147483647。
位运算符
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1,有一个为0,结果就是0 |
| | 或 | 两个位都为0时,结果才为0,有一个为1时结果是1 |
异或 | 两个位相同为0,不同为1 | |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各个二进制位向左移动若干位,高位丢弃,低位补0 |
>> | 右移 | 各个二进制位向右移动若干位,低位丢弃,高位补0(无符号数) |
>>> | 无符号右移 | 各个二进制位向右移动若干位,低位丢弃,高位补0(无符号数) |
对任意数进行按位非操作的结果为-(x+1)
对任意数x进行左移,结果为 x * 2^n
对任意数x进行右移,结果为 x / 2^n
二进制与十进制转换
使用toString
和parseInt
进行进制转换:
// 十进制-> 二进制
num.toString(2)
// 二进制-> 十进制
parseInt(num,2)
位运算的用途
- 取整 将小数与0求或运算
1.2 | 0 // 1
~~(1.2)
- 边界判断 假如我们要判断鼠标指针是否在容器内,会存在 上、下、左、右、左上、左下、右上、右下这8钟状态,这时我们可以将标志位与1、2、4、8相或可以获得这8种状态:
let flag = 0;
if (pos.left < left) flag = flag | 8;
if (pos.right > right) flag = flag | 2;
if (pos.bottom > bottom) flag = flag | 4;
if (pos.top < top) flag = flag | 1;
switch(flag) {
// 上
case 1:
// 右
case 2:
// 右上
case 3:
// 下
case 4:
// 右下
case 6:
// 左
case 8:
// 左上
case 9:
// 左下
case 12:
// code
}
假如我们有一系列控制开关,通过 a | b | c的形式要比 '{a: true, b: true, c: true}' 简单的多
- 判断奇偶数 奇数二进制最后一位位1,所以奇数 & 1 === 1
num & 1 // 1:奇数,0:偶数
4.判断系统权限
我们假设某个管理系统有a, b, c, d四级权限,其中不同帐号分别有不同的权限(可能有1个或多个),例如admin 账户有a + b +c +d 四级权限,guest用户有b + c权限。 我们把权限分别用0001, 0010, 0100, 1000表示(即最通俗的1,2,4,8),如果admin用户有a, b, c, d四种权限,则admin的权限为 1 | 2 | 4 | 8 = 15,而guest用户权限为 4 | 8 = 12, 则判断用户是否有某种权限可以如下判断:
admin & 4 === 4
admin & 8 === 8
admin & 2 === 2
admin & 1 === 1
- 切换变量0、1
var flag = 0
flag = flag ^ 1 // 1
flag = flag ^ 1 // 0
6.交换两个变量值
let a= 1,b=2;
a=a^b;
b=a^b;
a=a^b;
// b:1,a:2
- 结合indexOf判断
~arr.indexOf(..) // ~-1===0
8.rgb和16进制色值转换
将RGB的3个数值分别转为十六进制数,然后拼接,即 rgb(255, 255, 255) => '#' + 'FF' + 'FF' + 'FF'。 巧妙利用左移,我们把十六进制数值部分当成一个整数,即FFFFFF,我们可以理解为FF0000 + FF00 + FF, 如同我们上面解释,如果左移是基于十六进制计算的,则可以理解为FF << 4, FF << 2, FF, 而实际上我们转为二进制则变为 FF << 16,如下: rgb->hex
function RGBToHex(rgb){
// 取出rgb中的数值
let arr = rgb.match(/\d+/g);
if (!arr || arr.length !== 3) {
console.error('rgb数值不合法');
return
}
let hex = (arr[0]<<16 | arr[1]<<8 | arr[2]).toString(16);
// 自动补全第一位
if (hex.length < 6) {
hex = '0' + hex;
}
return `#${hex}`;
}
hex->rgb
function hexToRGB(hex){
if (!/^#([0-9a-fA-F]{3}){1,2}$/.test(hex)) {
console.error('颜色不合法');
return
};
// #f00 转为 #ff0000
if (hex.length == 4) {
hex = hex.replace(/([0-9a-fA-F])/g, '$1$1');
};
let num = hex.replace('#', '0x');
let r = num >> 16;
// 0xff = 255
let g = num >> 8 & 0xff;
let b = num & 0xff;
return `rgb(${r},${g},${b})`;
}