Javascript位运算

  数字电路是二进制的基础,数字电路高低电压状态代表二进制的0和1,比如低电平0~0.8V代表0,高电平3.7V~5.0V代表1, 这样的话64个触发器单元,就可以实现一个浮点寄存器来存储64位浮点数。 位运算相对乘法、乘方是较为偏向底层的计算,硬件上更接近寄存器,位运算也可以称为位操作,对寄存器进行位级操作。 比如一个整数在内存中以二进制的形式占32位,你可以直接操作每一位的0或1,查看下图示范。位bit是计算机最小存储单元, 音译称为比特位,8bit就是1字节(1B),由于靠近底层操作,位运算的执行效率比较高,Javascript和汇编语言、C语言一样都支持位运算。

位操作示意图

下面的内存中寄存器上存储的的二进制数00...0001010代表十进制的10。
00000000 00000000 00000000 0000 1010
解释器解析Javascript代码通过CPU控制内存进行位操作,直接把最后1位的0更改为1,寄存器上存储的的二进制数状态变为00...0001011代表十进制的11。
00000000 00000000 00000000 0001 1010

位运算符表格

符号 名称 含义 计算(二进制) 返回结果
& 按位与 按位与是每一位执行与操作,类比逻辑与&&操作 1 & 0 0
| 按位或 按位或是每一位执行或操作,类比逻辑或||操作 1 | 0 1
~ 按位非/取反 每一位与原来相反 ~ 1 0
^ 按位异或 当且仅当一个操作数为1,返回1,均为1或0均返回0 1 ^ 1 0
<< 左移 a << b表示操作数a每一位左移b位,右侧空出位置0 00000001 << 1 00000010
>> 有符号右移 a >> b表示操作数a每一位右移b位,超出位舍弃 00000011 >> 1 00000010
>>> 无符号右移 a >> b表示操作数a每一位右移b位,超出位舍弃,最左位置0 00000011 >>> 1 00000010

位运算赋值表格

  学习位运算赋值参考运算符中的其它赋值运算符和位运算,很容易明白,就不细细讲解,列一个表格,方便查询。

运算符 名称 语法 含义
<<= 左移赋值 a <<= b a = a << b
>>= 右移赋值 a >>= b a = a >> b
>>> 无符号右移赋值 a >>>= b a = a >>> b
& 按位与赋值 a &= b a = a & b
^= 按位异或赋值 a ^= b a = a ^ b
|= 按位或赋值 a |= b a = a | b

整型数据

  C语言中位运算仅支持整型和字符型数据,Javascript语言ECMAScript标准中没有字符型数据,处理文字数据类型的只有字符串类型, Javascript语言同样支持整型的位运算,Javascript整型数据和C语言int整型一样分配32位内存空间。 整型数据有无符号整型和有符号整型,无符号整型对应的是正整数0~232-1,有符号整型可以表示负整数、正整数-231~231-1。 Javascript语言统一默认为有符号整型定义内存存储的方式,数据类型看起来对应数学中的数字概念,实际上控制的是内存的存储方式,比如同样整数10,用有符号整型和 无符号整型分别定义它的存储,在内存中寄存器32位每一位的电压值是不一样的,抽象描述就是0和1分布状态是不一样的。

  整型数据以二进制补码的形式存储,正整数补码直接简单和负整数的补码相对绕一些。

有符号32位整型数据存储

0~231-1总计231个数字

   有符号类型的数据最高位(左边第一位)是符号位,当符号位为1时表示负数,最高位为0时表示正数。无符号整型数据内存中没有符号位,全部比特位用来存储数值, 这正是无符号整型称谓无符号三个字的来源,无符号对应的英文是unsigned。

有符号正整数存储格式

   把十进制书转换为二进制,左侧位全部置0,例如10的二进制形式是1010,内存中表现形式如下

   把十进制书转换为二进制,左侧位全部置0,例如10的二进制形式是1010,内存中表现形式如下

正整数10

00000000 00000000 00000000 0000 1010

   0符号位按照正整数的方式,全部位为0

0

0 0000 0000 0000 0000 0000 0000 0000 000

最大值231-1

0 1111 1111 1111 1111 1111 1111 1111 111

有符号负整数存储格式

-231-1~-1总计231个数字

负整数与内存对应效果如下,
-1 11111111 11111111 11111111 1111 1111
-2 11111111 11111111 11111111 1111 1110
-3 11111111 11111111 11111111 1111 1101
-4 11111111 11111111 11111111 1111 1100
... ........ ........ ........ ........
... ........ ........ ........ ........
最小值-231 1 0000 0000 0000 0000 0000 0000 0000 000

    负整数对应二进制形式计算方法,以-10为例

  1. -10对应正整数10

    00000000 00000000 00000000 0000 1010
  2. 10取反

    11111111 11111111 11111111 1111 0101
  3. 取反后加1

    11111111 11111111 11111111 1111 0110

按位与运算

 let a = 6;
 let b = 11;
 let c = a & b;

计算结果c的值为2

a的二进制补码 00000000 00000000 00000000 0000 0110
b的二进制补码 00000000 00000000 00000000 0000 1011
计算结果c 00000000 00000000 00000000 0000 0010

  按位与在实际计算中的作用就是可以对一段数据进行选择取舍 比如有一个表示颜色的十六进制的值“0xFFFFFF”,如果把它的红色R、绿色G成分滤掉,就可以利用按位与运算,不过要利用好参与运算的变量。

0xFFFFEE 00000000 1111 1111 1111 1111 1110 1110
0xFF 00000000 00000000 00000000 1111 1111
0xEE 00000000 00000000 00000000 1110 1110

  操作数0xFF的作用就是低8位,放行通过,操作数0xFFFFEE低8位的数据会完整保留下来0xEE,第9~32位的数据通通截杀,操作数0xFFFFEE代表红色和绿色的数据FF会过滤掉。 如果不太理解可以关注后面完整的案例。

位运算枚举

运算 结果
0 & 0 0
0 & 1 0
1 & 0 0
1 & 1 1

按位或运算

位运算枚举

运算 结果
0 | 0 0
0 | 1 1
1 | 0 1
1 | 1 1

按位异或运算

按位异或运算枚举

运算 结果
0 ^ 0 0
0 ^ 1 1
1 ^ 0 1
1 ^ 1 0

  异或运算的特点是“咱俩一样结果为零,咱俩不同结果为一”,比如你执行let x = x^x,结果是x置0,是“咱俩一样结果为零”的反应;

左移运算

  十进制数据整体左移一位代表数据整体放大10倍,二进制数据整体左移一位同理,意味着放大2倍, 说明右移可以实现原数据放大2n

15 00000000 00000000 00000000 1111
15x24 00000000 00000000 0000 1111 0000

(有符号)右移运算

  右移运算的特点是后面的数据后抛弃,也就是说可以用于抛弃一组数据的一部分,十进制数据整体右移一位代表数据整体缩小10倍,二进制数据整体右移一位同理意味着缩小2倍,

0xFFFFFF右移八位变为0x00FFFF

0xFFFFFF 00000000 1111 1111 1111 1111 1111 1111
右移8位 00000000 00000000 1111 1111 1111 1111

(无符号)右移运算“>>>”

  移动规则和有符号右移基本一样,不一样的地方是无论原来的数是正还是负,符号位均置0,也就是说无符号右移后负数变为正数,正数还是正数。

  右移运算的特点是后面的数据后抛弃,也就是说可以用于抛弃一组数据的一部分,十进制数据整体右移一位代表数据整体缩小10倍,二进制数据整体右移一位同理意味着缩小2倍,

-1 >>> 1结果是2^23-1
-1 1111 1111 1111 1111 1111 1111 1111 1111
结果 01111111 11111111 1111 1111 1111 1111
-2^31 >>> 1结果是2^30,也就是1073741824,十六进制形式0x40000000
结果 10000000 00000000 0000 0000 0000 0000
结果 01000000 00000000 0000 0000 0000 0000

实际应用案例

定义一个函数可以把十六进制整数作为参数,比如0xADDFDA,调用函数后可以拆分出来三个值0xAD、0xDF、0xDA,就是颜色的三种成分红绿蓝

    function setHex( hex ) {//hex一个十六进制数,代表颜色值RGB

    hex = Math.floor( hex );

    this.r = ( hex >> 16 & 255 ) / 255;//获取颜色值R部分
    this.g = ( hex >> 8 & 255 ) / 255;//获取颜色值G部分
    this.b = ( hex & 255 ) / 255;//获取颜色值B部分

    return this;
}