为什么位运算?
骚! 快!
因为在计算机中所有的数据都是以二进制的形式存储的,即0和1。
js位运算符工作于32位的数字上。任何数字操作都将转换为32位。结果会转换为 JavaScript 数字。
其他运算会通过编译器转换成机器语言执行,直接使用底层的语言就不需要编译器的转换,所以得到更高的执行效率,当然可读性可能会降低。
位运算符
运算符 | 描述 | 例子 | 类似于 | 结果 | 十进制 | 特点 |
---|---|---|---|---|---|---|
& | AND | x = 5 & 1 | 0101 & 0001 | 0001 | 1 | 都为1则为1,否则为0 |
| | OR | x = 5 | 1 | 0101 | 0001 | 0101 | 5 | 有1则为1,没1则为0 |
~ | 取反 | x = ~ 5 | ~0101 | 1010 | -6 | 加一取反 |
异或 | x = 5 ^ 1 | 0101 ^ 0001 | 0100 | 4 | 相同为0,不同为1 | |
<< | 左移 | x = 5 << 1 | 0101 << 1 | 1010 | 10 | 左移一位 |
>> | 右移 | x = 5 >> 1 | 0101 >> 1 | 0010 | 2 | 又移一位 |
其他好理解,重点聊一下取反和异或
取反 ~
“~”的原理:将内存中的补码按位取反(包括符号位)。
规则
- 二进制数在内存中是以补码的形式存放的。
- 补码首位是符号位,0表示正数,1表示负数。
- 正数的补码、反码,都是其本身。
- 负数的反码:符号位为1,其余各位求反,但末位不加1 。
- 负数的补码:符号位不变,其余各位求反,末位加1 。
- 所有的取反操作、加1、减1操作,都在有效位进行。
正数
~5
5二进制 0 0101
5补码 0 0101
5取反 1 1010
补码减1 1 1001
取反 1 0110
结果十进制 -6
~9
9二进制 0 1001
9补码 0 1001
9取反 1 0110
补码减1 1 0101
取反 1 1010
结果十进制 -10
~7
7二进制 0 0111
7补码 0 0111
7取反 1 1000
补码减1 1 0111
取反 1 1000
结果十进制 -8
负数
~-1
-1二进制 1 0001
-1反码 1 1110
-1补码(末位加一) 1 1111
-1取反 0 0000
结果十进制 0
~-2
-2二进制 1 0010
-2反码 1 1101
-2补码(末位加一) 1 1110
-2取反 0 0001
结果十进制 -1
异或 ^
特点
-
相同为0,不同为1,
-
满足交换律和结合律,
a ^ b = b ^ a
a ^ b ^ c = a ^ (b ^ c)
-
任何数异或自己都为0,因为相同为0
-
任何数和0异或都为自己,因为不同为1
说这个不是为了说废话,是为了说下边这个
// 交换数组i和j位置上的值
const swap = (arr, i, j)=> {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
为什么?
我们一步一步看
const swap = (arr, i, j)=> {
/**
走完第一步
arr[i] 等于 arr[i] ^ arr[j] arr[j] 任然等于arr[j]
*/
arr[i] = arr[i] ^ arr[j];
/**
第二步
arr[i] 任然等于 arr[i] ^ arr[j]
arr[j] 等于 arr[j] ^ arr[i] ^ arr[j], 因为满足交换律和结合律,
arr[j]可以转换为 arr[j] ^ arr[j] ^ arr[i], 因为任何数异或自己都为0,
所以 arr[j] 等于 0 ^ arr[i], ,又因为任何数和0异或都为自己,
所以 arr[j] 等于 arr[i]
*/
arr[j] = arr[i] ^ arr[j];
/**
同理,走完第三步
arr[j] 等于 arr[j]
arr[j] 等于 arr[i]
完成交换
*/
arr[i] = arr[i] ^ arr[j]; // arr[i] ^ arr[j] ^ arr[i] arr[i]
}
码字不易,技术有限,欢迎指正,点赞转发评论是最大的鼓励。