位运算的强大

位运算到底怎么实现它的价值

Posted by My on March 21, 2022

位运算,默默无闻而不被人关注,但却在 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次方,然后向下取整

细节还是挺多的,多使用就熟了。那么,你又是在哪用到了位运算呢?