2

1.位运算符的定义
位运算是在数字底层(即表示数字的 32 个数位)进行运算的。由于位运算是低级的运算操作,所以速度往往也是最快的(相对其它运算如加减乘除来说),并且借助位运算有时我们还能实现更简单的程序逻辑,缺点是很不直观,许多场合不能够使用。
2.关于二进制
ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数)。有符号整数使用 32 位的前 31 位表示整数值。第 32 位表示数值的符号,如 0 表示正,1 表示负。这
一位称为符号位(sign bit),它的值决定了数值其余部分的格式。正值以真正的二进制格式存储,即 31位中的每一位都代表 2 的幂。第一位(称为第 0 位)表示 20(2的0次方),第二位表示 21,依此类推。如果一个位是空的,则以0填充,相当于忽略不计,比如,数值18的二进制格式为00000000000000000000000000010010,或更精简的 10010。后者是用到的 5 个有效位,决定了实际的值,如下图
image
3.二进制和十进制
3.1js代码进行转化

// 十进制 => 二进制
let num = 10;
console.log(num.toString(2));
// 二进制 => 十进制
let num1 = 1001;
console.log(parseInt(num1, 2)); 

3.2手动转化

// 二进制 => 十进制

image

// 十进制 => 二进制,使用短除法

image.png
4.概况
image.png

5.详解及应用
5.1按位或 |
|与||操作符的道理也是一样的,只要两个数中有一个数为1,结果就为1,其他则为0
image.png
5.1.1 或|的应用场景
用或 | 操作,在实际应用中,我们可以实现向下取整,即int = num | 0,因为位运算是对整数操作的,当遇到浮点型的数值时,会先将浮点型的数据转成整型,然后再进行或运算,因为|只有其中一个数为1结果就是1,而0的二进制也是0,所以可实现向下取整

    3.7 | 0  //3
    1.1 | 0 //1

5.2按位与 &
&运算符表示只有两个数的值为1时,才返回1
image.png
5.2.1 &的应用场景
我们管理系统做表单checkbox多选是,加入我们有a、b、c、d四个chebox的选项,1,2,4,8分别代表checkbox的value值,当我们全选a + b + c + d的时候,可以将1 | 2 | 4 | 8 = 15传给java后台,如果只选择a + c的时候,则传递给java后台的值为1 | 4 = 5。
那么我们做编辑功能的时候,如何判断checkbox是否被选中?很简单,用&符号就可以,比如用户选择了a+c,后台给我们返回5,c的值为1,那么 4 & 5 = 4, b的选项我们是没有选择的,则 2 & 5 = 5,也就是说,选中的通过&的运算值为>0的值,没有选中的将会为0。

1 | 2 | 4 | 8 = 15
1 | 4 = 5
4 & 5 = 4
2 & 5 = 0

这种实现方式在做系统权限特别好友,不过要特别注意的是,checkbox的值必须为2的幂次方,并且最大值为2的31次方。
5.3 按位异或 ^
两个操作数相应的比特位有且只有一个1时,结果为1,否则为0

image.png
5.3.1应用场景
(1)假如我们通过某个条件来切换一个值为0或者1

    //普通的写法
    function update(toggle) {
        var num = toggle ? 1 : 0;
        console.log(num)
    }
    update(true);
    // 通过异或我们可以这么写
    num = num ^ 1;  //num为true的返回 0, 为 false返回1

(2)可以在不使用第三变量的值得情况下交换两个变量的值

    let a = 5,
        b = 6;

    a = a ^ b;
    b = a ^ b;
    a = a ^ b;

(3)可以实现简单的加密

const key = 313;
          function encryption(str) {
              let s = '';
              str.split('').map(item => {
                s += handle(item);
              })
              return s;
          }

          function decryption(str) {
            let s = '';
            str.split('').map(item => {
                s += handle(item);
            })
            return s;
          }

          function handle(str) {
              if (/\d/.test(str)) {
                return str ^ key;
              } else {
                let code = str.charCodeAt();
                let newCode = code ^ key;
                return String.fromCharCode(newCode);
              }
          }

          let init = 'hello world 位运算';
          let result = encryption(init);             // őŜŕŕŖęŎŖŋŕŝę乴軩窮
          let decodeResult = decryption(result);     // hello world 位运算

5.4 按位非~
按位非就是求二进制的反码的操作。对数值进行非运算主要有一下步骤:

(1)求数值二进制,如var num = 1; // 二进制 00000000000000000000000000000001
(2)取该值得反码为: num2 = 11111111111111111111111111111110
(3)第32位符号位的值不变,num2其他为位在取反码后位 num3 = 10000000000000000000000000000001
(4)然后加1为10000000000000000000000000000010,即-2,也就是1的按非~运算结果为-2

5.4.1应用场景

// 常用判断
if (arr.indexOf(item) > -1) {
    // code
}
// 按位非    ~-1 = - (-1 + 1)
if (~arr.indexOf(item)) {
    // code
}

5.5 左移 <<
该操作符会将第一个操作数向左移动指定的位数。向左被移出的位被丢弃,右侧用 0 补充。

例如 3 << 2 的运算图示如下:
3 = 0000 0000 0000 0000 0000 0000 0000 0011
12 = 0000 0000 0000 0000 0000 0000 0000 1100

5.6 有符号右移 >>
该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,拷贝最左侧的位以填充左侧。由于新的最左侧的位总是和以前相同,符号位没有被改变。所以被称作“符号传播”。

例如3>>2
3 = 0000 0000 0000 0000 0000 0000 0000 0011
0 = 0000 0000 0000 0000 0000 0000 0000 0000

5.7 无符号右移>>>
该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,左侧用0填充。因为符号位变成了 0,所以结果总是非负的。(译注:即便右移 0 个比特,结果也是非负的。)

    var num = -64; // 11111111111111111111111111000000(负数采用的是补码表示)
    num = num >>> 5; // 134217726
    a.64的补码是1000000
    b.取它的反码为11111111111111111111111110111111
    c.加1得她的补码为11111111111111111111111111000000
    d.无符号右移5位位00000111111111111111111111111110
   //求负整数的补码,将其原码除符号位外的所有位取反(0变1,1变0,符号位为1不变)后加1

image.png

6.总结
位运算是在数字底层进行操作的,运算速度快,书写简洁,但是很多场景没法使用,也不够直观。


带着代码去旅行
64 声望3 粉丝