0%

【位运算】实战篇

判断奇偶

我们看整数转为二进制数的时候,最后一位,如果是1必为奇数,如果是0则为偶数。所以我们用该数与1做与运算。结果为0则为偶,结果为1则为奇。

1
2
偶数 & 1 = 0
奇数 & 1 = 1

交换两个数

这是用到了异或的性质。a^b^a = 0^b = b

1
2
3
4
5
6
let a = 1;
let b = 2;

a = a^b;
b = a^b; // a^b^b = a;
a = a^b; // a^a^b = b;

取整

由于js在做位运算的时候,是吧浮点数转为整数来计算。 我们可以用这个原理通过左移0位来进行整数转换。

1
0.1 << 0  // 0

同理用>>0也行但是>>>0就不行。
还有根据a|0=a的原理|0也可以进行取整。

权限验证

这块就是最近工作中用到的。
位运算在权限系统中的使用。的保证每个权限码都是唯一的并且权限码的二进制数形式有且只有一位为1剩余的位为0(2^n)。

接下来我们就可以用

  1. | 来赋予权限
  2. & 来验证权限
1
2
3
4
5
6
7
8
9
10
11
12
let r = 0b100
let w = 0b010
let x = 0b001

// 给用户赋 r w 两个权限
let user = r | w
// user = 6
// user = 0b110 (二进制)

console.log((user & r) === r) // true 有 r 权限
console.log((user & w) === w) // true 有 w 权限
console.log((user & x) === x) // false 没有 x 权限

如果想删除一个权限的话,最简单的方式就是用异或^。但是异或是一个有则减,无则增的一个操作。

那么如果单纯的想删除权限(而不是无则增,有则减)怎么办呢?答案是执行 &(~code),先取反,再执行与操作

但是这种方法也有局限性。因为js操作最多只有32位。有一篇文章很好地解决了这个问题局限性和解决办法

做字符串的加密

这部分参考这篇文章深入研究js中的位运算及用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const key = 313;
function encryption(str) {
let s = '';
str.split('').map(item => {
s += handle(item);
})
return s;
}

function decryption(str) {
let s = '';
str.split('').map(item => {
s += handle(item);
})
return s;
}

function handle(str) {
if (/\d/.test(str)) {
return str ^ key;
} else {
let code = str.charCodeAt();
let newCode = code ^ key;
return String.fromCharCode(newCode);
}
}

let init = 'hello world 位运算';
let result = encryption(init); // őŜŕŕŖęŎŖŋŕŝę乴軩窮
let decodeResult = decryption(result); // hello world 位运算

用~~来取整

我们先看效果

1
2
3
4
5
6
7
8
9
10
11
12
13
~~1.1 // 1

首先在js中位运算要转为整数,所以先对1取反, 简单地用八位
1的源码 0000 0001
然后取反 ~ 0000 0001 = 1111 1110
发现符号位为负数。这里会将负数变为补码的形式,为了统一加法和减法,减法会变成加一个负数,而负数会以补码的形式存储。
1111 1110 的 补码为 反码 加 1
1000 0010
转为十进制,就成了-2,然后再对-2取反
这时就将负数转为补码的形式
1111 1110
然后在取反。就为
0000 0001 转为十进制 就为 1

我们其实可以看到,都是用补码来进行取反。(正数的补码是它本身,负数的补码为反码+1)

判断边界

做边界判断,总共有3种超出情况:右、上、左,并且可能会叠加,如鼠标在左上角的时候会导致左边和上面同时超出。需要记录超出的情况进行调整,用001表示右边超出,010表示上方超出,100表示左边超出,如下代码计算:

let postFlag = 0;
//右边超出
if(pos.right < maxLen) posFlag |= 1;
//上面超出
if(pos.top < maxLen) posFlag |= 2;
//左边超出
if(pos.left < maxLeftLen) posFlag |= 4;
//对超出的情况进行处理,代码略
switch(posFlag){
      case 1: //右
      case 2: //上
      case 3: //右上
      case 4: //左
      case 6: //左上
}

保留高位/低位

如想要保留后面四位 只需要用数跟 &0000 1111 这样前四位都变为了0。保留低位同理。