Javascript位运算
数字电路是二进制的基础,数字电路高低电压状态代表二进制的0和1,比如低电平0~0.8V代表0,高电平3.7V~5.0V代表1, 这样的话64个触发器单元,就可以实现一个浮点寄存器来存储64位浮点数。 位运算相对乘法、乘方是较为偏向底层的计算,硬件上更接近寄存器,位运算也可以称为位操作,对寄存器进行位级操作。 比如一个整数在内存中以二进制的形式占32位,你可以直接操作每一位的0或1,查看下图示范。位bit是计算机最小存储单元, 音译称为比特位,8bit就是1字节(1B),由于靠近底层操作,位运算的执行效率比较高,Javascript和汇编语言、C语言一样都支持位运算。
位操作示意图
下面的内存中寄存器上存储的的二进制数00...0000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
位运算符表格
符号 | 名称 | 含义 | 计算(二进制) | 返回结果 |
& | 按位与 | 按位与是每一位执行与操作,类比逻辑与&&操作 | 1 & 0 | 0 |
| | 按位或 | 按位或是每一位执行或操作,类比逻辑或||操作 | 1 | 0 | 1 |
~ | 按位非/取反 | 每一位与原来相反 | ~ 1 | 0 |
^ | 按位异或 | 当且仅当一个操作数为1,返回1,均为1或0均返回0 | 1 ^ 1 | 0 |
<< | 左移 | a << b表示操作数a每一位左移b位,右侧空出位置0 | 0000000 |
000000 |
>> | 有符号右移 | a >> b表示操作数a每一位右移b位,超出位舍弃 | 000000 |
000000 |
>>> | 无符号右移 | a >> b表示操作数a每一位右移b位,超出位舍弃,最左位置0 | 000000 |
000000 |
位运算赋值表格
学习位运算赋值参考运算符中的其它赋值运算符和位运算,很容易明白,就不细细讲解,列一个表格,方便查询。
运算符 | 名称 | 语法 | 含义 |
<<= | 左移赋值 | 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
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
0符号位按照正整数的方式,全部位为0
0
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
最大值231-1
0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
有符号负整数存储格式
-231-1~-1总计231个数字
-1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
-2 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
-3 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
-4 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
... | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
... | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . | . |
最小值-231 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
-
-10对应正整数10
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 -
10取反
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 -
取反后加1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0
负整数对应二进制形式计算方法,以-10为例
按位与运算
let a = 6; let b = 11; let c = a & b;
计算结果c的值为2
a的二进制补码 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
b的二进制补码 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 |
计算结果c | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
按位与在实际计算中的作用就是可以对一段数据进行选择取舍 比如有一个表示颜色的十六进制的值“0xFFFFFF”,如果把它的红色R、绿色G成分滤掉,就可以利用按位与运算,不过要利用好参与运算的变量。
0xFFFFEE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
0xFF | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
0xEE | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 |
操作数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 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
15x24 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
(有符号)右移运算
- 超出位全部舍弃
- 左侧的符号位和其它位一样照常移动
- 移动完成后,原数据是正数,符号位置0保持正数,原数据是负数,符号位置1保持负数,符号位可以传播下去
右移规则
右移运算的特点是后面的数据后抛弃,也就是说可以用于抛弃一组数据的一部分,十进制数据整体右移一位代表数据整体缩小10倍,二进制数据整体右移一位同理意味着缩小2倍,
0xFFFFFF右移八位变为0x00FFFF
0xFFFFFF | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
右移8位 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
(无符号)右移运算“>>>”
移动规则和有符号右移基本一样,不一样的地方是无论原来的数是正还是负,符号位均置0,也就是说无符号右移后负数变为正数,正数还是正数。
右移运算的特点是后面的数据后抛弃,也就是说可以用于抛弃一组数据的一部分,十进制数据整体右移一位代表数据整体缩小10倍,二进制数据整体右移一位同理意味着缩小2倍,
-1 >>> 1结果是2^23-1-1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
结果 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
结果 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
结果 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
实际应用案例
定义一个函数可以把十六进制整数作为参数,比如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; }