位运算,默默无闻而不被人关注,但却在 js 开发中发挥着举足轻重的作用。
什么是位运算
位运算(Bitwise Operations)的执行效率是非常快的,能给我们的开发带来很多方便。 位运算:「指的是将一个整数的二进制格式进行运算」。JS 中,如果对一个数据进行位运算,它首先会将其转换为一个整数(抹掉小数), 并且按照 32 位的整数二进制进行排列。
1
2
3
4
2.3 -> 2 -> 0000 0000 0000 0000 0000 0000 0000 0010
NaN -> 0
Infinity -> 0
-Infinity -> 0
借鉴 M MAX 的介绍,对位运算进行了一下总结。
位与运算 &
写法:「数1 & 数2」; 将两个整数的每一位进行比较,如果都为 1
,则结果为 1
,否则结果为 0
。 例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 1: 0000 0000 0000 0000 0000 0000 0000 0001
// 2: 0000 0000 0000 0000 0000 0000 0000 0010
// 一位一位地进行比较
// result: 0000 0000 0000 0000 0000 0000 0000 0000
console.log(1 & 2); //0
// 2:10
// 3:11
console.log(2 & 3); //10 -> 2
// 2:010
// 4:100
console.log(2 & 4); //000 -> 0
位或运算 |
写法: 「数1 |
数2」; 将两个整数的每一位进行比较,如果都为 0
,则结果为 0
,否则结果为 1
。 例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 1: 0000 0000 0000 0000 0000 0000 0000 0001
// 2: 0000 0000 0000 0000 0000 0000 0000 0010
// 一位一位地进行比较
// result: 0000 0000 0000 0000 0000 0000 0000 0011
console.log(1 | 2); //11 -> 3
// 2:10
// 3:11
console.log(2 | 3); //11 -> 3
// 2:010
// 4:100
console.log(2 | 4); //110 -> 6
位非运算 ~
写法:「~数」 ;将该整数按位取反,就是 1
变为 0
, 0
变为 1
。
例如:-1
1
2
3
4
5
// 负数,第一位,即符号位为1,则为负数。
真码:1000 0000 0000 0000 0000 0000 0000 0001
反码:1111 1111 1111 1111 1111 1111 1111 1110 //真码取反
补码:1111 1111 1111 1111 1111 1111 1111 1111 //反码加1 计算机的最终储存
例如:~1
1
2
3
4
5
6
7
1: 0000 0000 0000 0000 0000 0000 0000 0001
反码:1111 1111 1111 1111 1111 1111 1111 1101 //取反并减1
得到反码后,1变0️,0变1
console.log(~1); // -2
取反快速运算:-取反的数字 -1;例如,~2
,即 -2 - 2= -3 。~-2
,即-(-2) - 1 = 1。
快速取反:
1
console.log(~~4.434155); //4
异或运算 ^
写法:「数1 ^ 数2」;将两个整数按位进行比较,不同取 1
,相同取 0
。 例如:
1
2
3
1: 01
2: 10
console.log(1^2); // 11 -> 3
炫技交换变量:
1
2
3
4
5
6
let a = 2;
let b = 4;
a = a ^ b;
b = a ^ b;
a = a ^ b;
console.log("a,b", a, b); // 4 2
应用场景
位的叠加(位的开关)。
权限配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用 2 的倍数
const permission = {
read: 1, //读权限
edit: 2, //编辑(修改)权限
create: 4, //创建权限
};
// 用 二进制 来表示
const permission = {
read: 0b0001,
edit: 0b0010,
create: 0b0100,
};
权限判断:
1
2
3
4
5
6
7
8
9
10
11
// 保存可读 可写
const p = permission.read | permission.edit; // 011 -> 3;
//判断权限 p 是有 可读权限
const res = p & (permission.read === permission.read) ? "可读" : "不可读";
console.log("res", res); // 可读
// 保存可创建 可写
const g = permission.create | permission.edit; // 110 -> 6
//判断权限 g 是有 可读权限
const res1 = g & (permission.read === permission.read) ? "可读" : "不可读";
console.log("res1", res1); //不可读
去掉权限:
1
2
3
4
5
6
7
8
9
// 保存可读 可写
let p = permission.read | permission.edit; // 011 -> 3
//去掉权限
p = (p | permission.read) ^ permission.read;
//判断权限 p 是有 可读权限
const res = (p & permission.read) === permission.read ? "可读" : "不可读";
console.log("res", res); // 不可读
位移运算
- 左位移
<<
写法:数1
«数2
;将数字 1 的二进制(除符号位外),向左移动数字 2 的次数 例如:
1
2
3
4
5
console.log("3 << 1", 3 << 1); // 0011 -> 0110 -> 6;
结论:
// f = x << y
// f = x * 2^y
- 右位移:
>>
写法:数1
»数2
;将数字 1 的二进制(除符号位外),向右移动数字 2 的次数 例如:
1
2
3
4
5
6
7
console.log("3 >> 1", 3 >> 1); // 0011 -> 0001 -> 1;
结论:
// f = x >> y
// f = Math.floor(x/2*y)
// 除以2的y次方,然后向下取整
细节还是挺多的,多使用就熟了。那么,你又是在哪用到了位运算呢?