在看了前端早读课一篇《绘制随机不规则三角彩条——小谈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
