位运算
# 定义
- 按位操作符是用于数值的底层操作,也就是操作内存中表示数据的比特位。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