JavaScript 之 位运算

一、简介

​ JavaScript的位运算符是将进行运算的数字(八进制、十进制、十六进制等)转换为32位的二进制串,超过 32 位的数字会丢弃其最高有效位,只保留后32位二进制串。然后再对每一位进行运算。但运算后得出的结果,会再次转换成标准的十进制的数字,然后返回。

注意: 如果进行位运算的数字是负数,则我们需要使用该负数的32位二进制补码来进行位运算。同理,如果运算后的结果为正数,则运算后的二进制串就是结果数值的二进制形式,如果运算后的结果为负数,则运算后的二进制串是结果数值的二进制补码形式。

​ 位运算符通常在处理大整数、位掩码、图形、加密等情况下使用,在日常编程工作中并不常用,对于一般的数字运算,使用常规的算术运算符即可。

二、前置知识

1、JavaScript数字进制相关知识:
  • 十进制整数字面量由一串数字序列组成,且没有前缀 0。
  • 八进制的整数以 0(或 0O、0o)开头,只能包括数字 0-7。
  • 十六进制整数以 0x(或 0X)开头,可以包含数字(0-9)和字母 a~f 或 A~F。
  • 二进制整数以 0b(或 0B)开头,只能包含数字 0 和 1。
2、二进制补码

​ 二进制补码是一种表示有符号整数的方法。在计算机中,整数通常以二进制形式存储和处理。补码表示法通过将负数转换为正数的补码形式,使得加法和减法操作可以在计算机中使用相同的硬件电路进行处理。

​ 在二进制补码中,最高位用于表示符号位,0表示正数,1表示负数。对于正数,其补码与其原码相同。而对于负数,其补码的计算方式如下:

​ ① 将负数的绝对值转换为二进制原码形式。

​ ② 对该二进制数取反(即将0变为1,将1变为0)。

​ ③ 对取反后的二进制数加1。

​ 例如,假设我们要表示-5的补码。首先,将-5的绝对值转换为二进制,得到 00000101。然后对其取反,得到 11111010。最后,对取反后的二进制数加1,得到 11111011,这就是-5的补码表示。如果想要将补码转换为原数值,只需要倒序进行操作即可。

三、单运算符

基础代码:
    // 声明一个十六进制变量
    let a = 0x10;
    // 声明一个十进制变量
    let b = 10;
    // 声明一个八进制变量
    let c = 0o22;
    // 获取a、b、c的二进制
    console.log(a.toString(2)); // 10000
    console.log(b.toString(2)); // 1010
    console.log(c.toString(2)); // 10010
1、&(按位与)

​ 该运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行&(与)操作。如果对应位的两个数字都为1,则该位运算的结果就为1,否则为0

    // a、b、c 两两进行按位与运算 
    console.log(a & b); // 10000 & 1010 = 00000 = 0
    console.log(b & c); // 1010 & 10010 = 00010 = 2
    console.log(a & c); // 10000 & 10010 = 10000 = 16
    // 负数进行按位与运算 
    console.log(-5 & 5); // 11111011 & 00000101 = 00000001 = 1
    console.log(-10 & -18); // 11110110 & 11101110 = 11100110(补码) = -26 
2、|(按位或)

​ 该运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行|(或)操作。如果对应位的两个数字中存在一个以上的1,则该位运算的结果就为1,否则为0

    // a、b、c 两两进行按位或运算
    console.log(a | b); // 10000 | 1010 = 11010 = 26
    console.log(b | c); // 1010 | 10010 = 11010 = 26
    console.log(a | c); // 10000 | 10010 = 10010 = 18
		// 负数进行按位或运算
    console.log(-5 | 5); // 11111011 | 00000101 = 11111111(补码) = -1
    console.log(-10 | -18); // 11110110 | 11101110 = 11111110(补码) = -2
3、^(按位异或)

​ 该运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行^(异或)操作。如果对应位的两个数字不相同,则该位运算的结果就为1,否则为0

		// a、b、c 两两进行按位异或运算
    console.log(a ^ b); // 10000 ^ 1010 = 11010 = 26
    console.log(b ^ c); // 1010 ^ 10010 = 11000 = 24
    console.log(a ^ c); // 10000 ^ 10010 = 10 = 2
    // 负数进行按位异或运算
    console.log(-5 ^ 5); // 11111011 ^ 00000101 = 11111110(补码) = -2
    console.log(-10 ^ -18); // 11110110 ^ 11101110 = 00011000 = 24
4、~(按位非)

​ 该运算符表示将要进行运算的数字,先转换成32位的二进制串,再按位进行~(非)操作,也就是反转每一位,如果原位为1,则反转为0;如果原位为0,则反转为1

​ 但是要注意,在JavaScript中32 位有符号整数操作数根据补码运算规则进行反转,也就是说,最高有效位表示负数。

​ 在进行按位非运算时,任何数字 x 的运算结果都是 -(x + 1),例如,~-5 运算结果为 4。由于数字 ~-1~4294967295(2 ** 32 - 1)均使用 32 位表示形式,它们的运算结果均为 0

		// 进行按位非运算
    console.log(~a); // ~10000 = -17
    console.log(~b); // ~1010 = -11
    console.log(~c); // ~10010 = -19
		// 负数进行按位非运算
    console.log(~-5); // ~1111011(补码) = 0000100 = 4
    console.log(~-10); // ~11110110(补码) = 00001001 = 9
妙用:

我们可以通过~~n实现对数字的向下取整,对于正数,~~ 是向下取整,与 Math.floor() 的效果相同。对于负数,~~ 实际上是向零取整(也称为截断),与Math.ceil()的效果相同。也就是说:~~n的效果等同于n>0 ? Math.floor(n) : Math.ceil(n);

console.log(~~(28/10)); // 2
console.log(~~(-28/10)); // -2
console.log(Math.floor(28/10)); // 2
console.log(Math.floor(-28/10)); // -3
console.log(Math.ceil(28/10)); // 3
console.log(Math.ceil(-28/10)); // -2
5、<<(左移)

​ 该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向左移动运算符右侧数字的位数。由于向左移动,左边超出32的位数将会被清除,右边会空出位,填充0

​ 移动任意数字 x << y 位,得出 x * 2 ** y。 例如:9 << 3 等价于 9 * 2³ = 9 * 8 = 72

		// 进行左移运算
    console.log(a << 1); // 10000 << 1 = 100000 = 32
    console.log(b << 2); // 1010 << 2 = 101000 = 40
    console.log(c << 3); // 10010 << 3 = 10010000 = 144
    // 负数进行左移运算
    console.log(-5 << 1); // 11111011(补码) << 1 = 11110110(补码) = -10
    console.log(-10 << 2); // 11110110(补码) << 2 = 11101100(补码) = -40
6、>>(算术右移)

​ 该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。由于向右移动,右侧被移出的位会被丢弃,左侧空出的位会根据最左侧的符号位进行填充。

​ 因此负数进行算术右移的结果一定为负数,正数进行算术右移的结果一定为正数。

    // 进行算术右移运算
    console.log(a >> 1); // 10000 >> 1 = 01000 = 8
    console.log(b >> 2); // 1010 >> 2 = 0010 = 2
    console.log(c >> 3); // 10010 >> 3 = 00010 = 2
    // 负数进行算术右移运算
    console.log(-5 >> 1); // 11111011(补码) >> 1 = 11111101(补码) = -3
    console.log(-10 >> 2); // 11110110(补码) >> 2 = 11111101(补码) = -3
7、>>>(无符号右移)

​ 该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。由于向右移动,右侧被移出的位会被丢弃,左侧空出的位填充0

​ 因此无论正数还是负数,进行无符号右移的结果一定为非负数。

   // 进行无符号右移运算
    console.log(a >>> 1); // 10000 >>> 1 = 1000 = 8
    console.log(b >>> 2); // 1010 >>> 2 = 10 = 2
    console.log(c >>> 3); // 10010 >>> 3 = 10 = 2
    // 负数进行无符号右移运算
    console.log((-5 >>> 0).toString(2));
    console.log(-5 >>> 1); // 11111111111111111111111111111011(补码) >>> 1 = 0111111111111111111111111111101 = 2147483645
    console.log(-10 >>> 2); // 11111111111111111111111111110110(补码) >>> 2 = 00111111111111111111111111111101 = 1073741821
妙用:

我们可以通过(num1 + num2) >>> 1的方式来获取两个数字的中间值,通过右移一位,相当于将前面计算出的整数除以 2,然后获取其整数部分,其效果等同于Math.floor((start + end) / 2)

(3+4) >>> 1; // 3
Math.floor((3+4)/2); // 3

四、组合运算符

1、&=(按位与赋值)

​ 该组合运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行&(与)操作。然后再将运算后的结果赋值给运算符左边的变量。

    // a、b、c两两进行按位与赋值
    a &= b; // 10000 &= 1010 = 00000 = 0
    console.log(a); // 0
    b &= c; // 1010 &= 10010 = 00010 = 2
    console.log(b); // 2
    c &= a; // 10010 &= 00000 = 00000 = 0
    console.log(c); // 0
    // 负数进行按位与赋值
    let d = 10;
    d &= -5; // 1010 &= 11111011 = 1010 = 10
    console.log(d); // 10
2、|&(按位或赋值)

​ 该组合运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行|(或)操作。然后再将运算后的结果赋值给运算符左边的变量。

    // 进行按位或赋值
    a |= b; // 10000 |= 1010 = 11010 = 26
    console.log(a); // 26
    // 负数进行按位或赋值
    let e = 10;
    e |= -5; // 1010 | 11111011 = 11111111(补码) = -5
    console.log(e); // -5
3、^=(按位异或赋值)

​ 该组合运算符表示将两个进行运算的数字,先转换成32位的二进制串,再按位进行^(异或)操作。然后再将运算后的结果赋值给运算符左边的变量。

    // 进行按位异或赋值
    a ^= b; // 10000 ^= 1010 = 11010 = 26
    console.log(a); // 26
    // 负数进行按位异或赋值
    let f = 10;
    f ^= -5; // 1010 ^= 11111011 = 11110001(补码) = -15
    console.log(f); // -15
4、<<=(左移赋值)

​ 该组合运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向左移动运算符右侧数字的位数。最后再将运算的结果赋值给运算符左侧的变量。

​ 由于向左移动,左边超出32的位数将会被清除,右边会空出位,填充0

    // 进行左移赋值
    a <<= 1; // 10000 <<= 1 = 100000 = 32
    console.log(a); // 32
    // 负数进行左移赋值
    let g = -5;
    g <<= 1; // 11111011(补码) <<= 1 = 11110110(补码) = -10
    console.log(g); // -10
5、>>=(算术右移赋值)

​ 该组合运算符表示该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。最后再将运算的结果赋值给运算符左侧的变量。

​ 由于向右移动,右侧被移出的位会被丢弃,左侧空出的位会根据最左侧的符号位进行填充。因此负数进行算术右移的结果一定为负数,正数进行算术右移的结果一定为正数。

    // 进行算术右移赋值
    a >>= 1; // 10000 >>= 1 = 01000 = 8
    console.log(a); // 8
    // 负数进行算术右移赋值
    let h = -5;
    h >>= 1; // 11111011(补码) >>= 1 = 11111101(补码) = -3
    console.log(h); // -3
6、>>>=(无符号右移赋值)

​ 该组合运算符表示该运算符表示将运算符左侧要进行运算的数字,先转换成32位的二进制串,然后再向右移动运算符右侧数字的位数。最后再将运算的结果赋值给运算符左侧的变量。

​ 由于向右移动,右侧被移出的位会被丢弃,左侧空出的位填充0。因此无论正数还是负数,进行无符号右移的结果一定为非负数。

    // 进行无符号右移赋值
    a >>>= 1; // 10000 >>>= 1 = 1000 = 8
    console.log(a); // 8
    // 负数进行无符号右移赋值
    let i = -5;
    i >>>= 1; // 11111011(补码) >>>= 1 = 0111111111111111111111111111101 = 2147483645
    console.log(i); // 2147483645

五、参考资料

位运算符

运算符

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
JavaScript 中,位运算使用以下语法: 1. 按位与(AND):使用 `&` 运算符。它对两个操作数的每个对应位执行逻辑 AND 操作,并返回结果。例如: ```javascript const result = 5 & 3; // 1 ``` 在上述示例中,`5` 的二进制表示为 `101`,`3` 的二进制表示为 `011`,按位与运算后,得到二进制 `001`,即十进制的 `1`。 2. 按位或(OR):使用 `|` 运算符。它对两个操作数的每个对应位执行逻辑 OR 操作,并返回结果。例如: ```javascript const result = 5 | 3; // 7 ``` 在上述示例中,`5` 的二进制表示为 `101`,`3` 的二进制表示为 `011`,按位或运算后,得到二进制 `111`,即十进制的 `7`。 3. 按位异或(XOR):使用 `^` 运算符。它对两个操作数的每个对应位执行逻辑 XOR 操作,并返回结果。例如: ```javascript const result = 5 ^ 3; // 6 ``` 在上述示例中,`5` 的二进制表示为 `101`,`3` 的二进制表示为 `011`,按位异或运算后,得到二进制 `110`,即十进制的 `6`。 4. 按位非(NOT):使用 `~` 运算符。它对操作数的每个位执行逻辑 NOT 操作,并返回结果。例如: ```javascript const result = ~5; // -6 ``` 在上述示例中,`5` 的二进制表示为 `00000000000000000000000000000101`,按位非运算后,得到二进制 `11111111111111111111111111111010`,即十进制的 `-6`。 需要注意的是,位运算符操作的是操作数的每个位,因此操作数通常是整数类型。如果操作数不是整数,JavaScript 会在进行位运算之前将其转换为整数。 希望以上信息对你有所帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的小朱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值