你想知道的javaScript位运算:取反、异或位运算

478 阅读3分钟

为什么位运算?

骚! 快!

因为在计算机中所有的数据都是以二进制的形式存储的,即0和1。

js位运算符工作于32位的数字上。任何数字操作都将转换为32位。结果会转换为 JavaScript 数字。

其他运算会通过编译器转换成机器语言执行,直接使用底层的语言就不需要编译器的转换,所以得到更高的执行效率,当然可读性可能会降低。

位运算符

运算符描述例子类似于结果十进制特点
&ANDx = 5 & 10101 & 00010001 1都为1则为1,否则为0
|ORx = 5 | 10101 | 00010101 5有1则为1,没1则为0
~取反x = ~ 5 ~01011010 -6加一取反
异或x = 5 ^ 10101 ^ 00010100 4相同为0,不同为1
<<左移x = 5 << 10101 << 11010 10左移一位
>>右移x = 5 >> 10101 >> 100102又移一位

其他好理解,重点聊一下取反和异或

取反 ~

“~”的原理:将内存中的补码按位取反(包括符号位)。

规则

  • 二进制数在内存中是以补码的形式存放的。
  • 补码首位是符号位,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 

异或 ^

特点

  1. 相同为0,不同为1,

  2. 满足交换律和结合律,

    a ^ b = b ^ a
    a ^ b ^ c = a ^ (b ^ c)

  3. 任何数异或自己都为0,因为相同为0

  4. 任何数和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]
}

码字不易,技术有限,欢迎指正,点赞转发评论是最大的鼓励。