【javaScript】Javascript中的 位运算符 + 使用场景 + 性能对比 + 封装

一、位运算符

js包括以下位运算符:

'&': AND运算符
 ○ '|': OR运算符
 ○ '~': 取反
 ○ '^': 异或
 ○ '<<': 左移
 ○ '>>': 右移

位运算符用于32位的数字上, 任何的数字操作都将转为32位, 运算结果再转化为JS数字


二、区别(题外话)

&&&||| 不能混为一谈 需要详细了解可以看这篇文章 → &&和&、||和|的区别

这里大概总结一下:

&& 逻辑与  || 逻辑或  它们都是 逻辑运算符

&  按位与  |  按位或  它们都是 位运算符

2.1 与运算分为普通与(&)和短路与(&&)两种

①普通与:所有的判断条件都要判断.

②短路与:如果前面的判断返回了false,那么后面不再判断,最终结果就是false.

2.2 或运算分为普通或(|)和短路(||)或两种

①普通或:所有的判断条件都要判断.

②短路或:如果前面的判断返回了true,那么后面不再判断,最终结果就是true.


三、AND运算符’&’ – 按位与

3.1 ‘&’ 计算原理过程

按位与运算符 & 是双目运算符。 其功能是将参与运算的两数转成32位二进制后, 各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。

例如:

console.log(6&2); //输出2
console.log(10&5); //输出0

说明(下述文字中的二进制, 前面(28位)均为0, 所以省略了)

6的二进制位0110, 2的二进制位0010, 结果就是0010, 转成十进制也就是2


10的二进制位为1010, 5的二进制位0101, 所以结果为0000, 也就是0

3.2 ‘&’ 应用场景

这个运算符, 我一般用它来获取RGB颜色值中的蓝色, 例如:

var rgbStr = "#6abc3a";
//将字符串转成十进制整数
var rgb = parseInt(rgbStr.replace(/^\s*#|\s*$/g, ""), 16);
//获取颜色值中的蓝色
var b = rgb&0xff;
//输出蓝色值得16进制字符串
console.log(b.toString(16));//输出: 3a

四、OR运算符’|’ – 按位或

4.1 ‘|’ 计算原理过程

按位或运算符“|”是双目运算符。 其功能是将参与运算的两数转成32位二进制后, 各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。
例如:

console.log(6 | 2); //输出6
console.log(10 | 5); //输出15

说明(下述文字中的二进制, 前面(28位)均为0, 所以省略了)

6的二进制位0110, 2的二进制位0010, 结果就是0110, 转成十进制也就是6


10的二进制位为1010, 5的二进制位0101, 所以结果为1111, 也就是15

4.2 ‘|’ 应用场景

这个运算符可以用来对数值进行取证, 用起来应该比Math.floor、Math.ceil、parseInt 方便, 实例如下:

console.log(10.5 | 0);
console.log(10.3 | 0);
console.log(10.8 | 0);

以上的输出结果都是10

注意三点:

  • 无论正数还是负数, parseInt 只是删除数字的小数部分(也就是取整)
  • 对正数, 其运行的结果和Math.floor是一样的(向下取整)
  • 对负数, 其运行结果和Math.ceil是一样的(向上取证)

下面来个例子:

console.log(8.35 | 0); //输出: 8
console.log(Math.floor(8.35)); //输出: 8
console.log(Math.ceil(8.35)); //输出: 9
console.log(parseInt(8.35)); //输出: 8

console.log(-8.35 | 0); //输出: -8
console.log(Math.floor(-8.35)); //输出: -9
console.log(Math.ceil(-8.35)); //输出: -8
console.log(parseInt(-8.35)); //输出: -8

4.3 位运算符 ‘|’ 、’~~’ 与 Math.floor、parseInt 性能对比

var count = 1000000
var num = 2.58

// ------------ 测试 ~~ 的性能 ------------
console.time('~~');

for (let i = count; i>0; i--) {
    ~~num;
}
console.timeEnd('~~'); 

// ------------ 测试 | 的性能 ------------
console.time('|');

for (let i = count; i>0; i--) {
    num | 0;
}
console.timeEnd('|'); 

// ------------ 测试 Math.floor 的性能 ------------
console.time('Math.floor');

for (let  i = count; i > 0; i--) {
    Math.floor(num);
}
console.timeEnd('Math.floor');

// ------------ 测试 parseInt 的性能 -------------
console.time('parseInt');

for (let i = count; i > 0; i--) {
    parseInt(num);
}
console.timeEnd('parseInt');

测试结果:


看来在取整这方面位运算符,还是比其他两个方法在性能上还是有较大的差异的,就是可能在代码维护起来比较麻烦,不那么简明


五、取反运算符 ‘~’

5.1 ‘~’ 计算原理过程

取反运算符为单目运算符,具有右结合性。 其功能是对参与运算的数的各二进位按位取反

下面也举一个例子:

这里解说有点麻烦, 需要将所有的32位都列出来, 所以就不加说明了

注: 到目前为止, 我没有在编程里面用到此运算符


六、异或运算符’^’ – 按位异或

6.1 ‘^’ 计算原理过程

按位异或运算符“^”是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异(即对应位数值不相同, 也就是其中一个为1,另一个为0)时,结果为1。

例如:

console.log(6 ^ 2); //输出4
console.log(10 ^ 5); //输出15

说明(下述文字中的二进制, 前面(28位)均为0, 所以省略了)

6的二进制位0110, 2的二进制位0010, 结果就是0100, 转成十进制也就是4


10的二进制位为1010, 5的二进制位0101, 所以结果为1111, 也就是15

6.2 ‘^’ 应用场景

此运算符可以用来交换两个整型变量的值(不定义中间变量),如:

var a = 125, b = 10;
a = a ^ b;
b = a ^ b;
a = a ^ b;
console.log("a = "+ a + ", b = " + b); 
//输出: a = 10, b = 125

6.3 ‘^’ 在交换两个整数时 与 定义临时变量、ES6的解构赋值的性能对比

var first = 2,
second = 5,
count = 1000000,
temp;

// ------------ 测试 ^ 的性能 ------------
console.time('^');

for (let  i = count; i > 0; i--) {
    first = first ^ second;
	second = first ^ second;
	first = first ^ second;
}


console.timeEnd('^'); 

// ------------ 测试 定义临时变量 的性能 ------------
console.time('定义临时变量');

for (let  i = count; i > 0; i--) {
	temp = first;
	first = second;
	second = temp;
}

console.timeEnd('定义临时变量'); 

// ------------ 测试 ES6的解构赋值 的性能 ------------
console.time('ES6的解构赋值');

for (let  i = count; i > 0; i--) {
	[first,second] = [5,2]
}

console.timeEnd('ES6的解构赋值');

测试结果:
在这里插入图片描述
可以看出在交换两个数上,普通的定义临时变量来进行交换的平均性能是比位运算符号和解构赋值是快一点的,但是大伙可以测试一下单次的交换两个整型变量时,性能其实差距都差不多,而且可以忽略不计;所以在项目中,我们不可能像上面测试一样,动不动测个几十万次,现实中是不存在的;所以综上所述,我只是想说解构算是代码比较精简,并且相对容易理解的


七、右移运算符 ‘>>’

7.1 ‘>>’ 计算原理过程

右移运算符“>>”是双目运算符, 其功能是把“>>”左边的运算数的各二进位全部右移若干位(不足位补0),“>>”右边的数指定移动的位数。
右移n位的结果相当于整除2的n次方后的值
例如:

console.log(6>>2); //输出: 1
console.log(10>>2); //输出: 2

说明(下述文字中的二进制, 前面(28位)均为0, 所以省略了)

6的二进制位0110, 向右移两位, 结果就是0001, 转成十进制也就是1


10的二进制位为1010, 向右移两位, 所以结果为0010, 也就是2

说明一下:

m >> n, 相当于 m /(2^n) [ 这里的^不是异或运算符, 而是2的n次方 ], 转成js表达式就是 Math.floor(m / Math.pow(2, n)) ;

console.log(100>>4); //输出: 6
console.log(Math.floor(100 / Math.pow(2,4)));  //输出: 6
console.log(120>>3); //输出: 15
console.log(Math.floor(120 / Math.pow(2,3)));  //输出: 15

7.2 ‘>>’ 应用场景

目前我主要用于RGB颜色值中R(红色)的分离, 并配合按位与’&'运算符, 可以分离出G(绿色)
完整分离RGB颜色的代码如下

var rgbStr = "#6abc3a";
//将字符串转成十进制整数
var rgb = parseInt(rgbStr.replace(/^\s*#|\s*$/g, ""), 16);
//获取颜色值中的蓝色
var b = rgb&0xff;
var g = rgb>>8&0xff;
var r = rgb>>16;
//输出RGB/R/G/G色值的16进制字符串
console.log("rgb = " + rgb.toString(16));//输出: 6abc3a
console.log("r = " + r.toString(16));//输出: 6a
console.log("g = " + g.toString(16));//输出: bc
console.log("b = " + b.toString(16));//输出: 3a
//下面可以分别对R/G/B值进行计算, 如下(将颜色值变亮)
r = Math.min(r*1.3, 255);
g = Math.min(g*1.3, 255);
b = Math.min(b*1.3, 255);
//最后在将各个分量组合(要用到左移运算符)在一起, 就可以应用了

八、左移运算符 ‘<<’

8.1 ‘<<’ 计算原理过程

左移运算符“<<”是双目运算符。 其功能把“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0
左移n位就是乘以2的n次方
例如:

console.log(6<<2); //相当于6*(2*2), 输出: 24
console.log(10<<3); //相当于10*(2*2*2), 输出: 80

说明(下述文字中的二进制, 前面(28位)均为0, 所以省略了)

6的二进制位0110, 向左移两位, 结果就是00011000, 转成十进制也就是24


10的二进制位为1010, 向左移三位, 所以结果为01010000, 也就是80

8.2 ‘<<’ 应用场景过程

根据左运算符的特性常用于颜色分量的合成, 接着上述右移运算符的颜色例子:

var newRGB = (r<<16)+(r<<8)+(g)
//或者可以用按位或 '|'
//var newRGB = (r<<16)|(r<<8)|(g)

九、实现一个自定义颜色变亮/暗的函数

在这里插入图片描述
如上图所示,自定义传入颜色值(16进制值)、色彩的明暗程度比,点击对应按键实现对应效果【原生JS+位运算实现】

HTML:

<div class="container">
     <div id="rect"></div>
     <button id="light">加亮</button>
     <button id="reset">重置</button>
     <button id="dark">变暗</button>
 </div>

CSS:

.container {
    width: 250px;
    text-align: center;
    margin: 0 auto;
}

#rect {
    width: 120px;
    height: 65px;
    background-color: #fa71cd;
    margin: 10px auto;
}

button {
    margin-right: 20px;
}

JavaScript:

/**
 * @params {options}
 *	options.initColor → 自定义的颜色[16进制颜色值]
 *	options.lightRange → 明度比(1,2]
 *	options.lightRange → 暗度比(0,1)
 */
 function changeColor(options){
    var rect = document.getElementById("rect"),
    light = document.getElementById("light"),
    reset = document.getElementById("reset"),
    dark = document.getElementById("dark");

    const __DEFAULT__ = {
        initColor: '#fa71cd',
        lightRange: 1.1,
        darkRange: 0.9
    }
    if(options.lightRange < 1 || (options.darkRange > 1 || options.darkRange < 0)){
        console.error('Please pass in the correct value range - lightRange among (1,2] and darkRange among (0,1)')
    }
    var initColor = options.initColor || __DEFAULT__.initColor,
    newColor = initColor;
    rect.style.backgroundColor = initColor;

    light.addEventListener("click", function () {
        ld(options.lightRange || __DEFAULT__.lightRange, Math.min, 255);
    });
    dark.addEventListener("click", function () {
        ld(options.darkRange || __DEFAULT__.darkRange, Math.max, 0);
    });
    reset.addEventListener("click", function () {
        newColor = initColor;
        rect.style.backgroundColor = newColor;
    });

    function ld(factor, func, mom) {
        var obj = fl(newColor);
        console.log(factor,obj.r * factor,obj.g * factor,obj.b * factor)
        obj.r = func(obj.r * factor, mom) | 0;
        obj.g = func(obj.g * factor, mom) | 0;
        obj.b = func(obj.b * factor, mom) | 0;

        console.log(obj.r,obj.g,obj.b)
        newColor = "#" + hc(obj).toString(16);
        rect.style.backgroundColor = newColor;
    }
    //分离R/G/B
    function fl(rgb) {
        if (rgb.constructor == String)
            rgb = parseInt(rgb.replace(/^\s*#|\s*$/g, ""), 16);
        var obj = {};
        obj.r = rgb >> 16;
        obj.g = rgb >> 8 & 0xff;
        obj.b = rgb & 0xff;

        return obj;
    }
    //合并RGB
    function hc(obj) {
        console.log('xx', (obj.r << 16) | (obj.g << 8) | (obj.b))
        return (obj.r << 16) | (obj.g << 8) | (obj.b);
    }
}

调用方法,及其传参要求:

通过 changeColor(options) 调用,options为一个对象,其参数为:

参数 类型 是否必传
initColor String 否(默认为 ‘#fa71cd’)
lightRange Number 否(默认为 1.1)
darkRange Number 否(默认为 0.9)
 changeColor({
      initColor: '#fa71cd',
      lightRange: 1.2,
      darkRange: 0.8
  })

(撒花✿ ~ 完)


结语

感谢你认真的看到了最后,我相信你应该收获很多,我也很开心能帮到你,一起加油 ~ 如果文章有错误之处,请指点,小弟承蒙各位大佬的意见 ~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Umbrella_Um/article/details/102874300#_354

发布了134 篇原创文章 · 获赞 80 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Umbrella_Um/article/details/102874300