• JavaScript位运算符


       一、位运算符   

    位运算符处理 32 位数

    该运算中的任何数值运算数都会被转换为 32 位数,结果会被转换回 JavaScript 数

    位运算符用于直接对二进制进行计算,共有七个运算符:

    或运算(or):符号为 |,若两个二进制位都为0,则结果为0,否则为 1;

    与运算符(and):符号为 & ,若两个二进制位都为 1,则结果为1,否则为 0;

    否运算(not):符号为 ~ ,对一个二进制位取反;

    异或运算(xor):符号为 ^ ,若两个二进制位不相同,则结果为1,否为0。

    左移运算(left shift):符号为 << 

    右移运算(right shift):符号为 >> 

    带符号位的右移运算(zero filled right shift):符号为 >>> 

       二、位运算符简介  

    位运算符直接处理每一个比特位(bit),所以是非常底层的运算。

    好处:运算速度极快;

    缺点:运算很不直观,许多场合不能使用,否则会使代码难以理解和查错。

    注意位运算符只对整数起作用,如果一个运算子不是整数,则会自动转成整数后再执行

    在JavaScript内部,数值都是以64位浮点数的形式储存的,但是在做位运算的时候,是以32位带符号的整数进行运算的,并且返回的值也是一个32位带符号的整数。

       三、各位运算符的特点   

    (一)或运算 和 与运算

    逐个位数比较两个运算子

    或运算两个二进制位中只要有一个为 11,就返回 1,否则就返回 0;

    与运算:两个二进制位中只要有一位为 0,就返回为 0,否则就返回 1;

    0|3;     //3
    0&3;   //0
    3|4    //7
    3&4   //0

    在上面的 0和 3的表达式中,0和 3的二进制形式分别为 00 和 11,所以进行"或运算" 会得到 11(即3);进行“与运算” 会得到 00(即0);

    在上面的 3和 4的表达式中,3和 4的二进制形式分别为 11 和 001,所以进行"或运算" 会得到 111(即7);进行“与运算” 会得到 0(即0);

    注意:

    位运算只对整数有效,遇到小数时,会将小数部分舍去,只保留整数部分;

    所以将一个小数与 0 进行“或运算”,等同于对该数去除小数部分,即取整数位数。(这种取整方式不适用于超过二进制32位整数最大值 2147483648 的数)

    2.7|0; //2
    -3.5|0;  //-3
    
    2147483647.4 | 0;   //2147483647
    2147483648.4 | 0;   //-2147483648
    2147483649.4 | 0;   //-2147483647   

    (二)否运算

    否运算:将每个二进制位都变成相反数(即 0 变为 1,1 变为 0),运算机制不是很明显所以有时候会难以理解。

    ~ 3  // 4

    在这个表达式中对 3进行 ‘否运算’ 后,得到了-4。

    原因是位运算时,JavaScript 内部将所有的运算子都转换为 32位的二进制整数然后再进行运算。

    3 在JavaScript内部是 0000,0000,0000,0000,0000,0000,0000,0011(共32位),否运算后得到11111111111111111111111111111100 ,由于第一位是  1,所以这个数是负数。JavaScript内部对补码更详细的知识可以参考https://www.cnblogs.com/nyw1983/p/11887220.html)采取补码的形式表示负数,即需要将这个数减去1,再取一次反,然后加上符号,才能得到这个负数对应的十进制值。这个数减去1等于  11111111111111111111111111111011 ,再取一次反得到 000000000000000000000000000000100,加上负号就是 -4。

    这个过程这样计算未免太过麻烦,也可以简单记忆成,一个数与自身的取反值相加,等于 -1。(3的取反值为-4,-3的取反值是2)

    注意:

    1、对一个整数连续两次 ‘否运算’,可以得到它自身;

    2、所有的位运算只对整数有效。否运算遇到小数时,也会将小数部分舍去,只保留整数部分,所以,对一个小数连续两次否运算,就可以达到取整的效果。

    3、使用否运算取整,是所有取整方法中最快的一种。

    ~ -3 // 2
    ~~3 // 3
    ~~2.9 // 2
    ~~47.11 // 47
    ~~1.9999 // 1
    ~~3 // 3

    1、字符串类型:对字符串进行否运算,JavaScript引擎会先调用 Number 函数,将字符串转为数值。

    下面例子相当于  ~Number('011')

    ~'011' // -12
    ~'42 cats' // -1
    ~'0xcafebabe' // 889275713
    ~'deadbeef' // -1

    下面例子相当于  ~~Number('011')

    ~~'011'; // 11
    ~~'42 cats'; // 0
    ~~'0xcafebabe'; // -889275714
    ~~'deadbeef'; // 0

    2、数值的处理是:超出 32位的整数将会被截去超出的位数,NaN 和 Infinity 转为 0;

    3、对于其他类型的参数,否运算也是先用 Number 转为数值,然后再进行处理

    ~~[] // 0
    ~~NaN // 0
    ~~null // 0

    (三)异或运算

    异或运算:在两个二进制位不同时返回 1,相同时返回 0;

    0 ^ 3 // 3

    在这个表达式中,0 的二进制形式是 00,3的二进制形式是 11,它们每一个二进制位都不同,所以得到 11(即3)

    异或运算特殊运用:

    连续对两个数 a和b 进行 3 次异或运算(a^b, b^a, a^b),可以互换他们的值。这意味着,使用 异或运算 可以在不引用临时变量的前提下,互换两个变量的值。

     运用异或运算互换两个变量的值。这是互换变量值最快的方法。

    var a = 10;
    var b = 99;
    a ^= b, b ^= a, a ^= b;
    a // 99
    b // 10

    通过引用临时变量,互换两个变量的值。

    var a = 10;
    var b = 99;
    var c;
    c=a;a=b;b=c;
    console.log(a) // 99
    console.log(b) // 10

     异或运算也可以用来取整,与 0 一起运算

    12.9 ^ 0 // 12

    (四)左移运算符

    左移运算符:表示将一个数的二进制值向左移动指定的位数,在尾部补 0,即乘以 2 的指定次方(最高位即符号位不参加移动)

    // 4 的二进制形式为100,
    // 左移一位为1000(即十进制的8)
    // 相当于乘以2的1次方
    4 << 1
    // 8
    -4 << 1
    // -8

    这个例子中, -4左移一位得到 -8,是因为 -4 的二进制形式是 11111111111111111111111111111100 ,左移一位后得到 11111111111111111111111111111000,把这个数字转换为十进制(减去1后取反,再加上符号)结果为 -8;

    如果左移 0 位,就相当于将该数值转换为 32位整数,等同于取整,对于正数和负数 都有效。

    13.5 << 0
    // 13
    -13.5 << 0
    // -13

    左移运算用于二进制取值非常方便:

    实例:

    使用左移运算符,将颜色的RGB值转换为 HEX值


    var colo {r: 186, g: 218, b: 85};
    // RGB to HEX
    // (1 << 24)的作用为保证结果是6位数
    var rgb2hex = function(r, g, b) {
      return '#' + ((1 << 24) + (r << 16) + (g << 8) +
    .toString(16) .substr(1); }rgb2hex(color.r,color.g,color.b) // "#bada55"

    (五)右移运算符

    右移运算符:表示将一个数的二进制值向右移动指定的位数,头部补 0,即除以 2 的指定次方(最高位即符号位不参与移动)

    4 >> 1

     在这个例子中,因为 4的二进制形式为 00000000000000000000000000000100,向右移动一位之后(在头部补 0 ),得到 00000000000000000000000000000010,即为十进制的 2。

    -4 >> 1

     在这个例子中,-4 的二进制形式为 11111111111111111111111111111100,右移一位,头部补 1(二进制采取补码的形式表示负数),得到 11111111111111111111111111111100,即为十进制的 -2。

    使用右移运算符模拟 2 的倍数

    5 >> 1
    // 相当于 5 / 2 = 2,101转换为10,即为2
    21 >> 2
    // 相当于 21 / 4 = 5,10101转换为101,即为5
    21 >> 3
    // 相当于 21 / 8 = 2,10101转换为10,即为2
    21 >> 4
    // 相当于 21 / 16 = 1,10101转换为1,即为1

    (六)带符号位的右移运算符(>>>)

    带符号的右移运算符:表示将一个数的二进制形式向右移动,包括符号位也参与移动,头部补 0。

    所以,该运算总是得到正数。对于正数,该运算的结果与右移运算符(>>)完全一致,区别只要在于负数。

    4>>>1

    -4>>>1

    在这个例子中,-4 的二进制形式为 11111111111111111111111111111100,带符号位的右移一位,头部补0,得到 0111111111111111111111111111110,即为十进制的 2147483646。

    这个运算实际上将一个值转为 32 位无符号整数。

    查看一个负整数在计算机内部的储存形式,最快的方法就是使用这个运算符。

    -1>>>1

    -1 作为 32 位整数时,内部储存形式使用无符号正数格式解读,值为4294967295(即 ( 2^32)-1,等于11111111111111111111111111111111)

       四、设置对象属性开关   

    位运算符可以用作设置对象属性的开关

    加定某个对象有四个开关,每个开关都是一个变量。那么就可以设置一个四位的二进制数,它的每个位都对应一个开关。

    var flag_A = 1; // 0001
    var flag_B = 2; // 0010
    var flag_C = 4; // 0100
    var flag_D = 8; // 1000

    上面代码设置 A、B、C、D四个开关,每个开关分别占了有一个二进制位。然后,就可以使用 ‘与运算’ 检验,当前设置是否打开了指定开关。

    var flags = 5; // 二进制的0101
    if (flags & FLAG_C) {
     // ...
    }
    // 0101 & 0100 => 0100 => true

    上面代码检验是否打开了 开关C。如果打开,返回 true,否则返回 false。

    现在假设 需要打开ABD三个开关,可以构建一个掩码变量

    var mask = FLAG_A | FLAG_B | FLAG_D;
    // 0001 | 0010 | 1000 => 1011

    上面代码对A B D三个变量进行 ‘或运算’,得到掩码值为二进制的1011。有了掩码,‘或运算’ 可以确保打开指定的开关。

    flags = flags | mask;

    “与运算” 可以将当前设置中凡是与开关设置不一样的项,全部关闭

    flags = flags & mask;

    “异或运算” 可以切换(toggle)当前设置,即第一次执行可以得到当前设置的相反值,再执行一次,可以得到原来的值。

    flags = flags ^ mask;

    “否运算”可以翻转当前设置,即原设置为 0 ,运算后变为 1 ;原设置为 1 ,运算后变为 0;

    flags = ~flag
  • 相关阅读:
    洛谷P1085 不高兴的津津
    为什么要学习算法
    洛谷P1001 A+B Problem
    计算机问题求解周期
    洛谷P1000 超级玛丽游戏
    洛谷P1421 小玉买文具
    CF359D Pair of Numbers(ST+二分)
    2020.10.7
    2020.10.10
    2020.10.8
  • 原文地址:https://www.cnblogs.com/nyw1983/p/11880976.html
Copyright © 2020-2023  润新知