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
原文地址:https://www.cnblogs.com/nyw1983/p/11880976.html