目 录CONTENT

文章目录

搞懂JavaScript中的位运算符

俊阳IT知识库
2023-03-16 / 0 评论 / 0 点赞 / 411 阅读 / 2,303 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-03-16,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
广告

文章已同步至掘金:https://juejin.cn/post/7100867308867813389
欢迎访问😃,有任何问题都可留言评论哦~

前言

前段时间无意间在项目中看到了这么一行代码

 let x = m + n >> 1

虽然当时知道这是位运算符,但是对他的原理以及运算的结果还是有点不太清楚,遂搜集资料,整理了这篇文章

其实上面代码可以转化为:

let x = Math.floor((m + n) / 2)

逻辑运算符

运算符 描述
&& and
|| or
! not

位运算符

运算符 名称 描述
& AND 如果两位都是 1 则设置每位为 1
| OR 如果两位之一为 1 则设置每位为 1
^ XOR 如果两位只有一位为 1 则设置每位为 1
~ NOT 反转所有位
<< 零填充左位移 通过从右推入零向左位移,并使最左边的位脱落。
>> 有符号右位移 通过从左推入最左位的拷贝来向右位移,并使最右边的位脱落。
>>> 零填充右位移 通过从左推入零来向右位移,并使最右边的位脱落。

特点

  • 在Javascript中使用位运算符,底层会先将数字转为整数(因为JS中所有的数字都保存为双精度浮点数),然后再进行计算(比如经常利用位运算符取整)
  • 位运算直接对二进制位进行计算(即按照内存中表示数值的位来操作数值),直接处理每一个比特位
  • 位操作符并不直接操作64位的值。而是先将64位的值转换为32位的整数,再执行操作,最后再将结果转换为64位

对于有符号的整数,32位中的后31位用于表示整数的值,第1位表示数值的符号:0表示正数,1表示负数。

假如有一个值为55转换成二进制为 101 ,转换过程:

js-bitwise-operators-1

但是因为它占用4字节(32位),所以前面填了一堆0,即 00000000 00000000 00000000 00000101

那么 -5 呢?

在计算机中,负数是以其正值的补码形式来表达的,如下

原码:一个整数,按照绝对值大小转换成的二进制数,称为原码
比如:5 的原码为 00000000 00000000 00000000 00000101

反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码(1变0,0变1)
比如:5 的反码为 11111111 11111111 11111111 11111010

补码:反码加 1 称为补码
比如:5 的补码为 11111111 11111111 11111111 11111011

结论:

正数

正数以纯二进制格式存储,31位中的每1位都表示2的幂(还有1位是符号位)。第一位表示2º,第二位表示2¹,第三位表示2²,以此类推。没有用到的位以0表示(即忽略不计)。

负数

负数同样以二进制码存储,但使用的格式是二进制补码。计算一个数值的二进制补码(即求一个负数的二进制码),需要经过下列3个步骤:

  1. 求这个数值绝对值的二进制码
  2. 求二进制反码,即将0替换为1,将1替换为0
  3. 得到的二进制反码+1

原理

按位 与(AND):&

& 以 特定的方式 组合操作 二进制 数中对应的位, 如果对应的位都为1,那么结果就是1。如果任意一个位是0,则结果就是0

1 的二进制:00000000 00000000 00000000 00000001
2 的二进制:00000000 00000000 00000000 00000010

1 & 2 最终结果(也就是0):00000000 00000000 00000000 00000000

1 的二进制:00000000 00000000 00000000 00000001
3 的二进制:00000000 00000000 00000000 00000011

1 & 3 最终结果(也就是1):00000000 00000000 00000000 00000001

按位 或(OR):|

| 运算符跟 & 的区别在于如果对应的位中任一个操作数为1,那么结果就是1

1 的二进制:00000000 00000000 00000000 00000001
2 的二进制:00000000 00000000 00000000 00000010

1 | 2 最终结果(也就是3):00000000 00000000 00000000 00000011

1 的二进制:00000000 00000000 00000000 00000001
3 的二进制:00000000 00000000 00000000 00000011

1 | 3 最终结果(也就是3):00000000 00000000 00000000 00000011

按位 异或(XOR):^

^ 如果对应两个操作位 有且仅有1个1时结果为1,其它都是0

1 的二进制:00000000 00000000 00000000 00000001
2 的二进制:00000000 00000000 00000000 00000010

1 ^ 2 最终结果(也就是3):00000000 00000000 00000000 00000011

1 的二进制:00000000 00000000 00000000 00000001
3 的二进制:00000000 00000000 00000000 00000011

1 ^ 3 最终结果(也就是2):00000000 00000000 00000000 00000010

按位 非(NOT):~

~ 运算符是对位求反,1变0,0变1,也就是求二进制的反码

2 的二进制:00000000 00000000 00000000 00000010

反码:11111111 11111111 11111111 11111101
由于第一位(符号位)是1,所以这个数是一个负数。
JavaScript 内部采用补码形式表示负数,即需要将这个数减去1,再取一次反,然后加上负号,才能得到这个负数对应的10进制值。
如下:

反码 -1
11111111 11111111 11111111 11111100

取反
00000000 00000000 00000000 00000011

加负号
~2 的最终结果:-3

3 的二进制:00000000 00000000 00000000 00000011

反码:11111111 11111111 11111111 11111100

反码 -1
11111111 11111111 11111111 11111011

取反
00000000 00000000 00000000 00000100

加负号
~3 的最终结果:-4

左移(Left shift):<<

<< 运算符使指定的二进制数所有位都左移指定次数,其移动规则为:丢弃高位,低位补0(即按照二进制形式把所有的数字向左移动对应的位数,高位移除(舍弃),低位的空位补0)

1 的二进制:00000000 00000000 00000000 00000001

1 << 2 (即左移2位)
     00000000 00000000 00000000 00000001
   00000000 00000000 00000000 0000000100

抛弃向左移动的两个0,后面补0,结果二进制如下
     00000000 00000000 00000000 00000100
1 << 2 的最终结果:4

1 << 3 (即左移3位)
     00000000 00000000 00000000 00000001
  00000000 00000000 00000000 00000001000
  
抛弃向左移动的三个0,后面补0,结果二进制如下
     00000000 00000000 00000000 00001000
1 << 3 的最终结果:8

结论:

M << N,相当于:M * (2**N)

有符号右移:>> (又称 “符号传播”)

>> 该操作符会将指定操作数的二进制位向右移动指定的位数。向右被移出的位被丢弃,拷贝最左侧的位以填充左侧(这里是和无符号右移的最大区别)。由于新的最左侧的位总是和以前相同,而符号位并没有被改变。所以被称作“符号传播”

3 >> 2
3 的二进制:00000000 00000000 00000000 00000011

    00000000 00000000 00000000 00000011
      00000000 00000000 00000000 00000011
拷贝最前面两位00,抛弃向右移除的两位11,结果二进制如下
    00000000 00000000 00000000 00000000
3 >> 2 最终结果:0

4 >> 1
4 的二进制:00000000 00000000 00000000 00000100

   00000000 00000000 00000000 00000100
    00000000 00000000 00000000 00000100
拷贝最前面一位0,抛弃向右移除的一位0,结果二进制如下
   000000000 00000000 00000000 0000010
4 >> 1 最终结果:2

无符号右移:>>>

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,左侧用0填充(这点是和有符号右移最大的区别)。因为符号位变成了0,所以结果总是非负的。

注:对于非负数,有符号右移和无符号右移总是返回相同的结果。

3 >>> 2
3 的二进制:00000000 00000000 00000000 00000011

   00000000 00000000 00000000 00000011
     00000000 00000000 00000000 00000011
拷贝最前面两位00,抛弃向右移除的两位11,结果二进制如下
   00000000 00000000 00000000 00000000
3 >>> 2 最终结果:0

4 >>> 2
4 的二进制:00000000 00000000 00000000 00000100

   00000000 00000000 00000000 00000100
     00000000 00000000 00000000 00000100
拷贝最前面一位0,抛弃向右移除的一位0,结果二进制如下
   000000000 00000000 00000000 0000001
4 >>> 2 最终结果:1

应用

判断奇偶

// 偶数 & 1 = 0
// 奇数 & 1 = 1
console.log(2 & 1)    // 0
console.log(3 & 1)    // 1

取整

console.log(~~ 1.1)    // 6
console.log(1.1 >> 0)  // 6
console.log(1.1 << 0)  // 6
console.log(1.1 | 0)   // 6
// >>>不可对负数取整
console.log(1.1 >>> 0)   // 6

除以2向下取整

console.log(4 >> 1);
console.log(5 >> 1);

值交换

let m = 1
let n = 2
m ^= n
n ^= m
m ^= n
console.log(m)   // 2
console.log(n)   // 1

判断正负

console.log(1 === 1 >>> 0)
console.log(-1 === -1 >>> 0)

+_+

0

评论区