妙用
- 判断奇偶数
// 数值 & 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位的比特序列(用二进制形式表示)
- 所有操作数都会被转成有符号的32位整数,负数以补码形式表示(所有比特位取反后加1)
- 32位有符号整数所能表示的范围 [-2147483648, 2147483647]
- 超过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 |
逗号 | 左 -> 右 |
优先级高的先执行,优先级相同看关联性。评估的顺序始终是从左到右
不使用 +, -, ×, ÷ 实现四则运算
- 加法
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
}
- 减法
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
}
- 乘法
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) // 符号是否相同
}
- 除法
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)
}