位运算有啥用? 我是干前端的...鉴权?

358 阅读2分钟

前言: & | ^ ~ << >> 这些是啥? 有啥用? 跟前端有啥关系?

位运算在前端中的应用(权限管理)

const TEXT = 1; // 0b001
const ELEMENT = 1 << 1; // 0b010
const STYLE = 1 << 2; // 0b100

/*授权*/
/**0b100 | 0b010 = 0b110 */
let vnodeType = STYLE | ELEMENT;

/*鉴权*/
/**0b110 & 0b001 = 0b000 false */
vnodeType & TEXT // false
/**0b110 & 0b010 = 0b010 2 true */
vnodeType & ELEMENT // true
/**0b110 & 0b100 = 0b100 4 true */
vnodeType & STYLE // true

/*删除权限*/
/**0b110 ^ 0b010 = 0b100 */
vnodeType ^= ELEMENT

如果你没有掌握位运算的相关知识,可能会想到用数组来模拟, 或者用对象来存储;
那么权限操作的复杂度就到了O(n)之上, 而位运算则是O(1)的复杂度。

位运算概览

标题描述规则
&都为1时,为1 反之为0
|都为0时,为0 反之为1
异或相同时为0, 反之为1
(0b010 & 0b111).toString(2) // 0b010
(0b010 | 0b111).toString(2) // 0b111
(0b010 ^ 0b111).toString(2) // 0b101

牛刀小试,加深印象

LeetCode 231. 2 的幂

因为2^n一定是高位为1其他位都是0,例如:
0b10, 0b100, 0b1000
那么, 2^n - 1 则是:
0b01, 0b011, 0b0111
与操作符的运算规则是 都为1时为1,否则为0,
所以只要2^n和2^n-1相&之后为0,则一定是2的正整数幂次方

/*给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。
*/
exports.isPowerOfTwo = (n) => {
  while (n > 1) {
    n = n / 2;
  }
  return n === 1;
};

exports.isPowerOfTwo2 = (n) => {
  return n >= 1 && (n & (n - 1)) === 0;
};

LeetCode 136. Single Number

异或(^)运算规则是 相同时为0,否则为1
0b10 ^ 010 = 0b00
结论: 任何一个数跟自身 异或(^) 都为0

/*给定一个**非空**整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。*/
exports.singleNumber = (nums) => {
  let ret = 0;
  nums.forEach((n) => {
    ret ^= n;
  });
  return ret;
};

小结:

  • 异或运算(^), 任何数跟自身^都是0; 喜好0
  • 与(&): 喜好1
  • 或(|): 喜好0

  • 对位运算逐渐清晰, 不再畏惧位运算
  • 位运算原来还可以这么用

意外发现的疑惑点?

1<<2 === 2**2 // true
1<<30 === 2**30 // true
1<<31 === 2**31 // false

1<<31+1 ===1 // true

不是说JavaScript的最大安全数是$2^{53}$-1吗?为啥1<<31就溢出了?

参考