JavaScript按位操作符

940 阅读10分钟
原文链接: mp.weixin.qq.com
↑ 点击上方“J志道”关注 起初

在看了前端早读课一篇《绘制随机不规则三角彩条——小谈EvanYou个人主页的实现》的文章,里面有段代码:

<canvas></canvas>

<script>

    document.addEventListener('touchmove', function (e) {

        e.preventDefault()

    })

    var c = document.getElementsByTagName( 'canvas')[0],

    x = c.getContext('2d'),

    pr = window.devicePixelRatio || 1,

    w = window.innerWidth,

    h = window.innerHeight,

    f = 90,

    q,

    m = Math,

    r = 0,

    u = m.PI*2,

    v = m.cos,

    z = m.random

    c.width = w*pr

    c.height = h*pr

    x.scale(pr, pr)

    x.globalAlpha = 0.6

function i(){

    x.clearRect(0,0,w, h)

    q=[{x:0,y:h*.7+ f},{x:0,y:h*.7-f}]

    while(q[1].x<w+ f) d(q[0], q[1])

}

function d(i,j){

    x.beginPath()

    x.moveTo(i.x, i. y)

    x.lineTo(j.x, j. y)

    var k = j.x + (z()* 2-0.25)*f,

    n = y(j.y)

    x.lineTo(k, n)

    x.closePath()

    r-=u/-50

    x.fillStyle = '#'+(v( r)*127+128<<16 | v(r+ u/3)*127+128<<8 | v( r+u/3*2)*127+128). toString(16)

    x.fill()

    q[0] = q[1]

    q[1] = {x:k,y:n}

}

function y(p){

    var t = p + (z()*2- 1.1)*f

    return (t>h||t< 0) ? y(p) : t

}

    document.onclick = i

    document.ontouchstart = i

    i()

</script>

看到'#'+(v(r)*127+128<<16 | v(r+u/3)*127+128<<8 | v(r+u/3*2)*127+128).toString(16) 又出现了一直不太熟悉的位运算,趁着这个机会索性彻底的学习下。

位运算

按位操作符将其操作数当做32位的比特序列(由0和1组成),而不是十进制,十六进制或八进制数值。例如,是进制数7,用二进制表示则为111。按位操作符操作数字的二进制形式,但是返回值依然是标准的JavaScript数值。

有符号32位整数

所有的按位操作符的操作数都会被转为补码(为什么呢?)形式的有符号32位整数。补码形式是指一个数的负对应值(如5和-5)为数值的所有比特位翻转后,再加1。反转比特位即该数值进行‘非’位运算,也即该数值的反码。例如整数314的二进制编码:

00000000000000000000000100111010

~314,即314的反码:

11111111111111111111111011000101

最后,编码-314,即314的补码:

11111111111111111111111011000110

补码保证了当一个数是正数时,其最左边的比特位为0,当为负数时,其最左边的比特位为1。因此最左边的比特位被称为符号位(sign bit)。

0 是所有比特数字0组成的整数

00000000000000000000000000000000

-1 是所有比特数字1组成的整数

11111111111111111111111111111111

-2147483648 是除了最左边为1外,其他比特位都为0的整数

10000000000000000000000000000000

2147483648是除了最左边为0外,其他比特位都是1的整数

01111111111111111111111111111111

数字-2147483648和2147483648是32位有符号数字能表示的最小和最大整数。

按位逻辑操作符

按位逻辑操作符按遵守下面规则:

  • 操作符被转换成32位整数,用比特序列(0和1组成)表示。

  • 第一个操作数的每个比特位与第二个操作数的相应比特位匹配。

  • 位运算符应用到每对比特位,结果是新的比特值

&(按位与)

对每对比特位执行与AND操作。只有a和b都是1时,aANDB才是1。与操作的真值表如下:

a b a & b
0 0 0
1 1 1
0 1 0
1 0 0

9  = 00000000000000000000000000001001    

14 = 00000000000000000000000000001110                

14 & 9  = 00000000000000000000000000001000  = 8 

将任一数值x 与0 执行按位与操作,其结果都为0。将任一数值与-1执行按位与操作,起结果都为x。

| (按位或)

对每一位比特位执行或OR操作。如果a或b为1,则a OR b 结果为1。或操作的真值表:

a b a | b
0 0 0
1 1 1
0 1 1
1 0 1

9  = 00000000000000000000000000001001

14  = 00000000000000000000000000001110

14 | 9 = 00000000000000000000000000001111  = 15

将任一数值x与0进行按位或操作,其结果都是x。将任一数值与-1进行按位或操作,其结果都为-1

^ 按位异或

对每一对比特位执行异或XOR操作。当a和b不相同时,a XOR b结果为 1。异或操作真值表:

a b a ^ b
0 0 0
1 1 0
0 1 1
1 0 1

9  = 00000000000000000000000000001001

14  = 00000000000000000000000000001110

14 ^ 9 = 00000000000000000000000000000111  = 7

将任一数值与0进行异或操作,都结果为x。将任一数值x与-1执行异或操作,其结果都是~x。

~ 按位非

对每一个比特位执行非NOT操作。NOT a结果为a的反转(反码)。非操作的真值表:

a ~a 
0 1
1 0

9  = 00000000000000000000000000001001

~9 = 00000000000000000000000000001001

对任一数值x进行按位非操作的结果为-(x+1)。

按位移动操作符

按位移动操作符有两个操作数:第一个是要被移动的数字,而第二个要移动的长度。移动的方向根据操作符的不同而不同。

<< 左移

该操作符会将第一个操作数向左移动指定的位数。想左被移出的位被丢弃,右侧用0补充。

9  = 00000000000000000000000000001001

9 << 2 = 00000000000000000000000000100100 = 36

在数字 x 上左移y比特得到 x * 2y

>> 有符号右移

该操作符将第一个操作数向右移动指定的位数。向右被移出的位丢弃,拷贝最左侧的位以填充左侧。由于新的最左侧的位总是和以前相同 符号位没有被改变,所以被称作“符号传播”

9  = 00000000000000000000000000001001

9 >> 2 = 00000000000000000000000000000010 = 2

-9 >> 2 得到-3,因为符号被保留了

-9 =  11111111111111111111111111110111

-9 >> 2  11111111111111111111111111111101 = -3

>>> 无符号右移

该操作符会将第一个操作向右移动指定的位数。向右被移动的位被丢弃,左侧用0填充。因为符号位变成了0,所以结果总是非负的。 (即便右移0个比特位,结果也是非负的)

对于非负数,有符号和无符号右移总是返回相同的结果。9 >>> 2 得到2 和 9 >> 2 相同。

9  = 00000000000000000000000000001001

9 >>> 2 = 00000000000000000000000000000010 = 2

但是对于负数却不同。 -9 >>> 2 产生1073741821这和-9 >> 2不同

-9 11111111111111111111111111110111

-9 >>> 2 00111111111111111111111111111101 = 1073741821

我们在把数字转成二进制,除了Number.toString(2)。下面的代码也可以实现

function createBinaryString(nMask) {

    for (var nFlag = 0, nShifted = nMask, sMask = ''; nFlag < 32;

       nFlag++, sMask += String(nShifted >>> 31), nShifted <<=1)

    return sMask

}

应用技巧

判断奇偶数

x & 1 === 0

将任一数值x 与1 执行按位与操作,其结果为0时,x为偶数;结果为1时,x则为奇数

2 = 10

2 & 1 = 10 & 01 = 0 

向下取整

x | 0

x(浮点数) 浮点数是不支持位运算的,所以会先把浮点数x转换成整数再进行位运算。

0.618 | 0 = 0

向上取整

~~x

~~x = -(-(x + 1) +1)

~~0.618 = 0

交替变量

let a = 123, b = 321

a ^= b

b ^= a

a ^= b

判断正负

x === x >>> 0   

x右移0个比特位,结果也是非负的

参考:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators

https://github.com/YIXUNFE/blog/issues/63