JS高级程序设计位操作符

1、按位非:操作数的负值减一

2、位操作符包含,与,或,非,异或,左移、有符号右移,无符号右移

简介

JS中的所有数值都是以IEEE 754 64位格式存储,但是位操作并不直接对64位数值进行操作的,而是先把数值转换位32位整数,再进行对应的运算,之后再把结果转换位64位,因此我们在进行位运算的时候,就只需要考虑32位整数就行了。

按位非NOT(~)

  • 按位非的作用是对位取反,就是1变成0,0变成1,跟取二进制反码的差别是反码符号位不变,而按位非的符号位也要取反。

  • 按位非的最终结果始终对应的是~x = (-x) - 1;

使用场景取整

按位非可以用来取整:如~~3.14 = 3;因为位运算操作的是整数,会忽略掉小数部分;

按位与AND(&)

  • 按位与需要两个操作数,在两个数对应位都是1的时候才返回1
例1:25 & 3
    
25 = 0000 0000 0000 0000 0000 0000 0001 1001
 3 = 0000 0000 0000 0000 0000 0000 0000 0011
--------------------------------------------
 & = 0000 0000 0000 0000 0000 0000 0000 0001
result:25 & 3 = 1
     
例2:-25 & 3
-25 = 1111 1111 1111 1111 1111 1111 1110 0111
  3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
  & = 0000 0000 0000 0000 0000 0000 0000 0011
result:3
// 注意负数要先求补码再进行位运算
复制代码

使用场景1: 判断奇偶数

奇数 & 1     // 1
偶数 & 1     // 0
// 因为1的二进制只有最后一位为1,其余都是0,而偶数的二进制最后一位必为0,奇数最后一位必为1。
复制代码

使用场景2: 判断是否为2的整数幂

可以通过 n & (n - 1) 来判断
let a = 20;
let b = 32;
a & (a - 1)  // 16   a不是2的整数幂
b & (b - 1)  // 0    b是2的整数幂
复制代码

如下所示,2的整数幂的数的二进制都是1后面跟若干个0,那么当这个数减去1(0000 0001)之后,原本1的那一位就会变为0而原本1后面的就会变成1,这样再使用&,得到的结果就一定为0

0000 0001 -> 1    // 2^0
0000 0010 -> 2    // 2^1
0000 0100 -> 4    // 2^2
0000 1000 -> 8    // 2^3
复制代码

这个方法能用于解决 leetcode的231题

按位或OR(|)

  • 按位或是将两个操作数的二进制的每一位进行对比,只要其中一个为1就返回1,否则返回0;
18 | 5
18 -> 0001 0010
 5 -> 0000 0110
22 -> 0001 0110
复制代码

使用场景1: 取整

1.11 | 0 // 1
2.2542 | 0 // 2
// 主要还是利用位运算操作的是整数这一点来进行取整
复制代码

按位异或XOR(^)

  • 按位异或是将两个操作数的二进制的每一位进行对比,当两个数不一样时,返回1,否则返回0;
例1:25 ^ 3
    
25 = 0000 0000 0000 0000 0000 0000 0001 1001
 3 = 0000 0000 0000 0000 0000 0000 0000 0011
--------------------------------------------
 & = 0000 0000 0000 0000 0000 0000 0001 1010
result:25 ^ 3 = 1
复制代码

使用场景1: 取整

1.11 ^ 0 // 1
2.2542 ^ 0 // 2
// 同上,主要还是位运算操作的是整数这一点来进行取整
复制代码

使用场景2: 比较两个整数(或小数的整数部分)是否相等

1 ^ 1 // 0
5 ^ 5 // 0
1.23 ^ 1.432 // 0
2.34 ^ 3.34 // 1
复制代码

使用场景3: 完成值交换

let a = 1;
let b = 2;
a = a ^ b;
b = b ^ a;
a = a ^ b;
console.log(a, b) // 2, 1
// 可以这么用,但不如解构赋值方便
复制代码

使用场景4: 判断两个数符号是否相同

var a = 5;
var b = 2;
var c = -3;
console.log((a ^ b) >= 0); // true
console.log((a ^ c) >= 0); // false
复制代码

主要原理还是对他的符号位进行按位异或操作,如果符号位都是0,就返回0,此时得到的数字必定大于等于0,如果符号位一个为0,一个为1,返回1,此时得到的数字必定小于等于0,最后若符号位都为1,则返回0,结果也是大于等于0的

左移(<<)

  • 将数值的二进制码按照指定的位数向左移动,符号位不变
例:2 << 5
2 -> 0000 0010
     0100 0000
--------------
result: 64;
复制代码

左移的计算规则可以总结为:x << y = x * 2^y; 使用场景1: 取整

1.11 << 0 // 1
2.2542 << 0 // 2
复制代码

左移也可以用来取整,同时,下面的有符号右移(>>)无符号右移(>>>)也可以用来取整。

有符号右移(>>)

  • 将数值的二进制码按照指定的位数向右移动,符号位不变
例1:2 >> 5
2 -> 0000 0010
     0000 0000
--------------
这里的2转换成二进制后向右移动5位被自动省略了,所以:最终结果是0;

例2:64 >> 5
2 -> 0100 0000
     0000 0010
--------------
result: 2;
复制代码

有符号右移的计算规则可以总结为:x >> y = x / 2^y;

无符号右移(>>>)

  • 将数值的二进制码按照指定的位数向右移动,符号位也会跟着移动,整数在无符号右移的情况下跟有符号右移的结果是相同的,但是负数的话因为符号位也会右移,有时候会导致最终结果非常大。
例:-1 >>> 1
-1 -> 1111 1111 1111 1111 1111 1111 1111 1111
      0111 1111 1111 1111 1111 1111 1111 1111
---------------
result: 2147483647
复制代码

使用位运算进行权限管理

  1. 使用左移(<<)创建权限
const test1 = 1 << 0    // 以8位二进制表示:00000001
const test2 = 1 << 1    // 00000010
const test3 = 1 << 2    // 00000100
const test4 = 1 << 3    // 00001000
const test5 = 1 << 4    // 00010000
const test6 = 1 << 5    // 00100000
复制代码
  1. 使用按位或(|)分配权限
const user1 = test1;                                 // 00000001
const user2 = test1 | test2;                         // 00000011
const user3 = test1 | test2 | test3;                 // 00000111
const user4 = test1 | test3 | test4;                 // 00011001
const user5 = test6 | test3 | test4 | test5;         // 00111100
复制代码

3.使用按位与(&)校验权限

console.log((user1 & test1) > 0) // true;
console.log((user5 & test6) > 0) // true;
console.log((user5 & test1) > 0) // false;
console.log((user3 & test2) > 0) // true;
复制代码

JS的位运算要比普通运算更快吗

JS中的位运算并不一定会比较快,因为js引擎对位运算是模拟的,不是直接调用cpu进行运算的,和C语言等其他底层语言不一样,这取决于JS引擎的优化,目前来说,两种运算方式差不多处于同个量级。可运行如下代码查看一下:

console.time('test1');
console.log(Math.round(-3.434));
console.timeEnd('test1');

console.time('test2');
console.log(~~-3.434);
console.timeEnd('test2');
复制代码

总结一下

  • 位运算都是先将数值转换成32位二进制进行操作的。
  • JS中位运算并不一定会比普通运算更快。
  • 取反(~),按位或(|),按位异或(^),左移(<<),有符号右移(>>),无符号右移(>>>)都可以对小数取整,取整原理就是位运算只操作32为整数,忽略小数部分。
  • 在对NaNInfinity进行位运算时,这两个值会被当成0处理。
  • 对不是number类型的数据进行位运算时,会先调用Number()方法进行转换,再进行操作。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值