位运算
# 定义
- 按位操作符是用于数值的底层操作,也就是操作内存中表示数据的比特位。ECMA Script中的数值以IEEE 754 64位格式存储, 但是位运算符并不直接应用到64位表示,而是先把值转换为32位整数,再进行位操作。之后再将结果转换64位
 - 有符号整数使用 32 位的前 31 位表示整数值。第 32 位表示数值的符号,如 0 表示正,1 表示负
 - 正值以真正的二进制格式存储,即 31 位中的每一位都代表 2 的幂。第一位(称为第 0 位)表示 20 ,第二位表示 21 ,依此类推
 - 负值以一种称为二补数(或补码)的二进制编码存储 (下列是二补数的转换步骤:)
 

- 转换二进制
 
console.log(22..toString(2))
 1
# 按位非
# 1. 定义
- 按位非操作符用波浪符(~) 表示
 - 它的作用是返回一个数值的补码
 - 按位非是ECMAScript 中为数不多的几个二进制数学操作符之一
 
let num1 = 25
let num2 = ~num1 // 步骤1:将数值25转换为二进制 . 步骤2:获取二进制的反码。 步骤3:在反码的基础上 + 1
console.log(num2) // -26
 1
2
3
2
3
- 通过上述的实例可以看出,其结果就是
数值取反,减1 
# 2. 案例
const index = arr.indexOf('-1')
// 判断是否有值
if (~index) {
}
 1
2
3
4
5
6
2
3
4
5
6
- 案例分析:
- 如果使用API
indexOf的结果是-1的话,那么使用按位非后就是数值0 - 转换过程:-1 => 取反 - 1 => 0
 
 - 如果使用API
 
# 按位与
按位与操作符用和号(&)表示,有两个操作数。本质上,按位与就是将两个数的每一个位对齐, 然后基于真值表中的规则,对每一位执行相应的与操作
| 第一个数值的位 | 第二个数值的位 | 结果 | 
|---|---|---|
| 1 | 1 | 1 | 
| 1 | 0 | 0 | 
| 0 | 1 | 1 | 
| 0 | 0 | 0 | 
const result = 25 & 3
console.log(result) // 1
// 11001 & 00011 = 00001
 1
2
3
4
2
3
4
# 按位或
# 1. 定义
按位或操作符用管道符(|)表示,同样有两个操作数。按位或遵循如下真值表
| 第一个数值的位 | 第二个数值的位 | 结果 | 
|---|---|---|
| 1 | 1 | 1 | 
| 1 | 0 | 1 | 
| 0 | 1 | 1 | 
| 0 | 0 | 0 | 
- 按位或操作在至少一位是 1 时返回 1,两位都是 0 时返回 0
 
const result = 25 | 3
console.log(result) // 27
// 11001 & 00011 = 11011
 1
2
3
4
2
3
4
# 2. 经典实例(权限判断)
可以使用按位与 以及按位或来进行权限校验。
按位或进行赋值。按位与进行校验
- 读权限
let r = 0b100/ 写权限let w = 0b010/ 执行权限let x = 0b001 
// 1. 首先给用户赋值全部权限
const r = ob100
const w = 0b010
const x = 0b001
let limit = r | w | x // 0b111
// 2. 此时用户拥有读/写权限
let user = r | w
console.log((user & r) === r) // true
console.log((user & w) === w) // true
console.log((user & x) === x) // false
// 3. 如何删除权限呢(将对应权限的值 从 1 修改 为 0)。 可以使用异或(无进制 相加)
user = user ^ r
console.log((user & r) === r) // false
console.log((user & w) === w) // true
console.log((user & x) === x) // false
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 按位异或(无进制相加)
# 1. 定义
按位异或用脱字符(^)表示,同样有两个操作数。下面是按位异或的真值表
| 第一个数值的位 | 第二个数值的位 | 结果 | 
|---|---|---|
| 1 | 1 | 0 | 
| 1 | 0 | 1 | 
| 0 | 1 | 1 | 
| 0 | 0 | 0 | 
- 按位异或与按位或的区别是,它只在一位上是 1 的时候返回 1(两位都是 1 或 0,则返回 0)。
 
# 2. 经典案例:
# 2.1 将特定的一位从1 到 0 (删除权限)
let info = 0b1010011 // 想将内容修改为 0b01010001
const result = info ^ 0b0000010
console.log(result) // 0b01010001
 1
2
3
2
3
# 2.2 特殊特征
- 任何一个数 跟 0 异或 都是其自身值 N ^ 0 === N
 
4 ^ 0 // 4
 1
- 任何值 跟 其本身异或 都是 0 N ^N === 0
 
4 ^ 4 // 0
 1
# 2.3 经典用法
- 一个数组中存在偶数个相同的值(偶数个),但是只有一个奇数。 求奇数:
 - 例如:
[1,1,2,2,2,4,4,3]请求出现奇数的数字是谁 
const arr1 = [1, 1, 2, 2, 2, 2, 4, 4, 3]
let res
arr1.forEach(item => {
  res ^= item
})
console.log(res)
 1
2
3
4
5
6
7
2
3
4
5
6
7
# 左移 <<
# 1. 定义
- 左移操作符用两个小于号(<<)表示,会按照指定的位数将数值的所有位向左移动。比如,如果数 值 2(二进制 10)向左移 5 位,就会得到 64(二进制 1000000)
 - 注意,左移会保留它所操作数值的符号。比如,如果2 左移 5 位,将得到64,而不是正 64
 
let oldValue = 2
let newValue = oldValue << 5 // 64
 1
2
2
# 2. 根本
左移动(<<)的根本就是:N << S 数值N * (S个2相乘)
# 右移 >>
# 1. 定义
有符号右移由两个大于号(>>)表示,会将数值的所有 32 位都向右移,同时保留符号(正或负)。 有符号右移实际上是左移的逆运算
let oldValue = 64
let newValue = oldValue >> 5
console.log(newValue) // 2
 1
2
3
2
3
# 2. 根本
右移动(>>)的根本就是:N >> S 数值N / (S个2相乘)
# 进制转换
- 任意十进制转换任意(二)进制
 
22..toString(2) // '10110'
 1
- 任意进制转换十进制
 
parseInt('10110', 2) // 22
 1