按位操作符整理2

256 阅读13分钟

妙用

  • 判断奇偶数
// 数值 & 1 结果: 偶数为 0, 奇数为 1
(4 & 1) === 0 // 偶数 % 2 = 0
(5 & 1) === 1 // 奇数 % 2 = 1
  • 取整
// ~~ | << >> >>>
~~2.7     // 2
2.7 | 0   // 2
2.7 << 0  // 2
2.7 >> 0  // 2
2.7 >>> 0 // 2

~~-2.7     // -2
-2.7 | 0   // -2
-2.7 << 0  // -2
-2.7 >> 0  // -2
-2.7 >>> 0 // 4294967294
  • 四舍五入
// 数值 + 0.5 后取整
2.2 + 0.5 | 0 // 2
2.7 + 0.5 | 0 // 3
  • 比较两个数是否相等
// x ^ x => 0
(123 ^ 123) === 0 // true 相等
(123 ^ 124) === 0 // false 不相等
  • 切换0/1
// 如果 x 默认 0
x ^= 1 // 1 => 0 => 1 => ..
  • 检查符号是否相同
(3 ^  4) >= 0 // true 相同
(3 ^ -4) >= 0 // false 不同
  • 检索字符串
// ~-1 === 0
~'abc'.indexOf('c') ? 'c 包含在 abc 中' : 'c 不包含在 abc 中' // 'c 包含在 abc 中'
~'abc'.indexOf('d') ? 'd 包含在 abc 中' : 'd 不包含在 abc 中' // 'd 不包含在 abc 中'
  • 判断正负数
// >>> 优先级比 === 高
 123 ===  123 >>> 0  // true 正数
-123 === -123 >>> 0 // false 负数
  • 色值 rgb 转换成 hex
// <<
'#' + (255 << 16 | 68 << 8 | 0).toString(16) // #ff4400
  • 色值 hex 转换成 rgb
// >> [&]
`rgb(${0xff4400 >> 16}, ${0xff4400 >> 8 & 0xff}, ${0xff4400 & 0xff})` // rgb(255, 68, 0)
  • 权限系统(组合开关)
const r = 4 /* 100 */, w = 2 /* 010 */, x = 1 /* 001 */
let user = 0
// 赋予最高权限
user = r | w | x // 111
// 移除写和执行权限
user &= ~(w | x) // 111 & (~011) => 111 & 100 => 100
// 检查读/写权限
user & r // === r
user & w // === 0
// 切换读和执行权限
user ^= (r | x) // 100 ^ 101 => 001
user ^= (r | x) // 001 ^ 101 => 100
  • 值交换
let a = 2 /* 10 */, b = 3 /* 11 */
a ^= b // b => 11, a => 10 ^ 11 = 01
b ^= a // a => 01, b => 11 ^ 01 = 10
a ^= b // b => 10, a => 01 ^ 10 = 11

介绍

按位操作符会将其操作数转换成32位的比特序列(用二进制形式表示)

  1. 所有操作数都会被转成有符号的32位整数,负数以补码形式表示(所有比特位取反后加1)
  2. 32位有符号整数所能表示的范围 [-2147483648, 2147483647]
  3. 超过32位的数字会被丢弃
 0 => 00000000000000000000000000000000
-1 => 11111111111111111111111111111111

-1的转换过程:
 1 => 00000000000000000000000000000001
~1 => 11111111111111111111111111111110
+1 => 11111111111111111111111111111111

-2147483648 => 10000000000000000000000000000000 => -0x80000000
 2147483647 => 01111111111111111111111111111111 =>  0x7fffffff

超过32位的数字
转换前: 11100110111110100000000000000110000000000001 // 15872588537857
转换后:             10100000000000000110000000000001 // -1610588159

& (按位与)

1 & 1 // => 1
1 & 0 // => 0
0 & 0 // => 0

(x &  0) === 0 // x & 00000000000000000000000000000000
(x & -1) === x // x & 11111111111111111111111111111111

(9 & 14) === 8 // 1001 & 1110 => 1000

| (按位或)

0 | 0 // => 0
1 | 0 // => 1
1 | 1 // => 1

(x |  0) === x  // x | 00000000000000000000000000000000
(x | -1) === -1 // x | 11111111111111111111111111111111

(9 | 14) === 15 // 1001 | 1110 => 1111

             (1 | 0) === 1
           (1.6 | 0) === 1
             (0 | 0) === 0
            (-1 | 0) === -1
          (-1.9 | 0) === -1
            ([] | 0) === 0
            ({} | 0) === 0
          (null | 0) === 0
     (undefined | 0) === 0
          (true | 0) === 1
         (false | 0) === 0
           (NaN | 0) === 0
      (Infinity | 0) === 0
(function () {} | 0) === 0
        ('abcd' | 0) === 0
        ('9999' | 0) === 9999
        (1.23e2 | 0) === 123
       (-1.23e3 | 0) === -1230

^ (按位异或)

0 ^ 0 // => 0
1 ^ 0 // => 1
1 ^ 1 // => 0

(x ^  x) ===  0 // 110101 ^ 110101 => 0
(x ^  0) ===  x // 110101 ^ 000000 => 110101
(x ^ -1) === ~x // 110101 ^ 111111 => 001010

(9 ^ 14) === 7 // 1001 ^ 1110 => 0111

~ (按位取反)

1 // 反码 => 0
0 // 反码 => 1

~x === -(x + 1) // -x - 1 <= x ^ -1

 9 ===   0b00000000000000000000000000001001 // 00000000000000000000000000001001
~9 === ~~0b11111111111111111111111111110110 // 11111111111111111111111111110110
~9 === -10 // 00000000000000000000000000001010(10) => 11111111111111111111111111110101(取反) => 11111111111111111111111111110110(+1)

~-1 === 0

<< (左移)

x << y === x * (2 ** y)

9 << 2 === 36 // 00000000000000000000000000001001 => 00000000000000000000000000100100
9 << 3 === 72 // 00000000000000000000000001001000

>> (有符号右移)

// 向右被移出的位被丢弃,拷贝最左侧的位以填充左侧
 9 >> 2 === 2  // 00000000000000000000000000001001 => 00000000000000000000000000000010
-9 >> 2 === -3 // 11111111111111111111111111110111 => 11111111111111111111111111111101
-9 >> 0 === -9

-9 >> 2 === ~(9 >> 2) === ~(9 / 2 ** 2)

>>> (无符号右移)

// 向右被移出的位被丢弃,左侧用0填充
 9 >>> 2 === 2          // 00000000000000000000000000001001 => 00000000000000000000000000000010
-9 >>> 2 === 1073741821 // 11111111111111111111111111110111 => 00111111111111111111111111111101
-9 >>> 0 === 4294967287

-9 >>> 2 === -9 >>> 0 >>> 2

附录

方法

  • 进制转换
(20).toString(2)     // '10100'
parseInt('10100', 2) // 20
  • 自动化掩码创建
function createMask(...args) {
  // 最大 32 位
  let len = args.length < 32 ? args.length : 32
  let mask = 0
  while (len--) {
    mask |= args[len] << len // 参数靠前的放低位,靠后的放高位
  }
  return mask
}
createMask(true, true, false, true) // 1000 => 1010 => 1011 => 11
createMask(false, false, true) // 100 => 4
  • 逆算法:从掩码得到布尔数组
function arrayFromMask(mask) {
  //  0x7fffffff => 01111111111111111111111111111111
  // -0x80000000 => 10000000000000000000000000000000
  if (mask > 0x7fffffff || mask < -0x80000000) {
    throw new TypeError('arrayFromMask - out of range')
  }
  const arr = []
  while (mask) {
    arr.push(!!(mask & 1)) // 记录下mask最右边1位
    mask >>>= 1 // 保存后右移删除
  }
  return arr
}
arrayFromMask(11) // 1011 => [true, true, false, true]
arrayFromMask(4)  // 0100 => [false, false, true]
  • 将掩码转换成二进制字符串表示
function createBinaryString(mask = 0) {
  //  2147483647 => 01111111111111111111111111111111
  // -2147483648 => 10000000000000000000000000000000
  if (mask > 2147483647 || mask < -2147483648) {
    throw new TypeError('arrayFromMask - out of range')
  }
  let sMask = ''
  for (let i = 32; i--;) {
    sMask += String(mask >>> 31) // 记录下mask最左侧1位
    mask <<= 1 // 保存后左移删除
  }
  return sMask;
}

createBinaryString(11) // 00000000000000000000000000001011
createBinaryString(4)  // 00000000000000000000000000000100
const padZero = (num, len) => (Array(len).join(0) + num).slice(-len)
padZero(123, 8)        // '00000123'
'123'.padStart(8, '0') // '00000123'

运算符优先级和关联性

优先级 运算符 运算类型 关联性
21 (子表达式) 分组
---- ---- ---- ----
20 对象.属性 成员访问 左 -> 右
20 对象[属性] 计算成员访问 左 -> 右
20 new 构造函数(参数) 类实例化
20 函数(入参) 函数调用 左 -> 右
20 引用?.属性 可选链接 左 -> 右
---- ---- ---- ----
19 new 构造函数 实例化 左 -> 右
---- ---- ---- ----
18 操作数++ 后置自增
18 操作数-- 后置自减
---- ---- ---- ----
17 !操作数 逻辑非 左 <- 右
17 ~操作数 按位取反 左 <- 右
17 +操作数 一元加 左 <- 右
17 -操作数 一元减 左 <- 右
17 ++操作数 前置自增 左 <- 右
17 --操作数 前置自减 左 <- 右
17 typeof 操作数 对象类型 左 <- 右
17 void 表达式 忽略返回值 左 <- 右
17 delete 对象属性 删除对象属性 左 <- 右
17 await 表达式 等待异步回调 左 <- 右
---- ---- ---- ----
16 操作数 ** 操作数 左 <- 右
---- ---- ---- ----
15 操作数 * 操作数 左 -> 右
15 操作数 / 操作数 左 -> 右
15 操作数 % 操作数 左 -> 右
---- ---- ---- ----
14 操作数 + 操作数 左 -> 右
14 操作数 - 操作数 左 -> 右
---- ---- ---- ----
13 操作数 << 操作数 按位左移 左 -> 右
13 操作数 >> 操作数 按位右移 左 -> 右
13 操作数 >>> 操作数 按位无符号右移 左 -> 右
---- ---- ---- ----
12 操作数 < 操作数 小于 左 -> 右
12 操作数 <= 操作数 小于等于 左 -> 右
12 操作数 > 操作数 大于 左 -> 右
12 操作数 >= 操作数 大于等于 左 -> 右
12 属性 in 对象 属性是否在对象或其原型链中 左 -> 右
12 对象 instanceof 构造函数 构造函数是否出现在对象的原型链中 左 -> 右
---- ---- ---- ----
11 操作数 == 操作数 相等 左 -> 右
11 操作数 != 操作数 不等 左 -> 右
11 操作数 === 操作数 全等 左 -> 右
11 操作数 !== 操作数 非全等 左 -> 右
---- ---- ---- ----
10 操作数 & 操作数 按位与 左 -> 右
9 操作数 ^ 操作数 按位异或 左 -> 右
8 操作数 | 操作数 按位或 左 -> 右
7 操作数 && 操作数 逻辑与 左 -> 右
6 操作数 || 操作数 逻辑或 左 -> 右
5 操作数 ?? 操作数 空位合并(null,undefined) 左 -> 右
4 条件 ? 符合条件表达式 : 其他 条件表达式 左 <- 右
3 x = y 赋值 左 <- 右
3 x += y x = x + y 左 <- 右
3 x -= y x = x - y 左 <- 右
3 x *= y x = x * y 左 <- 右
3 x /= y x = x / y 左 <- 右
3 x %= y x = x % y 左 <- 右
3 x **= y x = x ** y 左 <- 右
3 x <<= y x = x << y 左 <- 右
3 x >>= y x = x >> y 左 <- 右
3 x >>>= y x = x >>> y 左 <- 右
3 x &= y x = x & y 左 <- 右
3 x ^= y x = x ^ y 左 <- 右
3 x |= y x = x | y 左 <- 右
3 x &&= y x && (x = y) 左 <- 右
3 x ||= y x || (x = y) 左 <- 右
3 x ??= y x ?? (x = y) 左 <- 右
2 yield 表达式 暂停/恢复生成器函数 左 <- 右
2 yield* 生成器或对象 迭代操作数 左 <- 右
1 表达式1 , 表达式2 逗号 左 -> 右

优先级高的先执行,优先级相同看关联性。评估的顺序始终是从左到右

不使用 +, -, ×, ÷ 实现四则运算

  1. 加法
function add2 (a, b) {
    if (a === 0 || b === 0) {
        return a ^ b
    }
    return add2(a ^ b, (a & b) << 1) // 递归
}
// 非递归
function add3 (a, b) {
    let t = 0
    while (b) {
        t = a ^ b
        b = (a & b) << 1
        a = t
    }
    return a
}
  1. 减法
function sub1 (a, b) {
    return add3(a, add3(~b, 1)) // a + (-b) ; -b = ~b + 1
}
function sub2 (a, b) {
    if (!b) return a
    const c = a & b
    // 把 a 和 b 相同位置为 1 的都消去
    a ^= c
    b ^= c
    return sub2(a | b, b << 1) // 递归
}
// 非递归
function sub3 (a, b) {
    while (b) {
        const c = a & b
        a ^= c
        b ^= c
        a |= b
        b <<= 1
    }
    return a
}
  1. 乘法
function negative (a) {
    return add3(~a, 1)
}
function mult (a, b) {
    let x = a < 0 ? negative(a) : a
    let y = b < 0 ? negative(b) : b
    let res = 0
    while (y) {
        if ((y & 1) === 1) {
            res = add3(res, x)
        }
        x <<= 1
        y >>= 1
    }
    return (a ^ b) >= 0 ? res : negative(res) // 符号是否相同
}
  1. 除法
function div1 (a, b) {
    const x = a < 0 ? negative(a) : a
    const y = b < 0 ? negative(b) : b
    if (x < y) return 0
    return (a ^ b) >= 0 ? add3(div1(sub3(a, b), b), 1) : sub3(div1(add3(a, b), b), 1) // 递归
}
// 非递归
function div2 (a, b) {
    const y = b < 0 ? negative(b) : b
    let x = a < 0 ? negative(a) : a
    let count = 0
    while (x >= y) {
        x = sub3(x, y)
        count = add3(count, 1)
    }
    return (a ^ b) >= 0 ? count : negative(count)
}
// 减倍数
function div3 (a, b) {
    if (a === 0 || b === 0) return 0
    const y = b < 0 ? negative(b) : b
    let x = a < 0 ? negative(a) : a
    let res = 0
    for (let i = 31; i >= 0; i = sub3(i, 1)) {
        if ((x >> i) >= y) {
            res = add3(res, 1 << i)
            x = sub3(x, y << i)
        }
    }
    return (a ^ b) >= 0 ? res : negative(res)
}

参考: