概览
本文详细剖析JavaScript的位运算符,其涉及的计算机原理和操作效果。
然后从实战的角度出发,罗列相关的应用场景。
位操作符概览
运算符 |
描述 |
示例 |
按位与(AND) |
两个操作数对应的比特位都是1时,结果才为1,否则为0 |
1011 & 0111 = 0011 |
按位或(OR) |
两个操作数对应的比特位至少有一个1时,结果为1,否则为0 |
1011 | 0111 = 1111 |
按位异或(XOR) |
两个操作数对应的比特位有且只有一个1时,结果为1,否则为0 |
1011 ^ 0111 = 1100 |
按位非(NOT) |
逐个反转操作数的比特位,即0变成1,1变成0 |
~1011 = 0100 |
左移 |
通过从右推入零向左位移,并使最左边的位脱落。 |
1011 << 1 = 10110 |
有符号右移 |
通过从左推入最左位的拷贝来向右位移,并使最右边的位脱落。 |
01011 >> 1 = 00101 |
无符号右移 |
通过从左推入零来向右位移,并使最右边的位脱落。 |
01011 >>> 1 = 00101 |
位操作支持多少位?
js只支持32位二进制数的位操作,也即能处理的最大十进制数字是 4294967295
。
1
| parseInt('11111111111111111111111111111111', 2);
|
验证下超过32位二进制数的位操作:
1 2 3 4 5 6
| parseInt('111111111111111111111111111111111', 2);
8589934591 >>> 0; 4294967295 >>> 0;
|
可以看出,数字 8589934591
和 4294967295
进行 无符号位右移0位
操作,得到的结果是一样的。
产生这样结果的原因,是js的位操作实现,只支持32位
。
注意:ECMAScript
中的所有数值都以IEEE754
64位格式存储,只是在位操作的时候 ,需要转换成32位进行操作。
负数的无符号右移
-2 >>> 1
为什么输出2147483647
?
-2
在运算中,是用补码表示,即1 1111111111111111111111111111110
。
其中,第1位是符号位。 符号位1
代表当前数字是负数。
将-2
无符号右移1位,则最右边的0
脱落,剩下31位1111111111111111111111111111111
,接着,在左侧补0
,得到01111111111111111111111111111111
。
01111111111111111111111111111111
代表十进制数2147483647
。
状态控制
场景:
以下使用React
+TypeScript
,实现游戏状态机的状态流转,根据状态渲染对应的操作按钮。
1 2 3 4 5 6 7
| export enum GAME_STATE{ INIT = 1 << 0, JOIN = 1 << 1, PREPARE = 1 << 2, PLAY = 1 << 3, }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| function RenderButton({state, changeState}){ if((state & GAME_STATE.PLAY) === GAME_STATE.PLAY){ return null; } if((state & GAME_STATE.INIT) === GAME_STATE.INIT){ return <button onClick={() => changeState(GAME_STATE.JOIN)} >加入游戏</button> } if((state & GAME_STATE.JOIN) === GAME_STATE.JOIN){ return <button onClick={() => changeState(GAME_STATE.PREPARE)} >准备游戏</button> }
if((state & GAME_STATE.PREPARE) === GAME_STATE.PREPARE){ return <button onClick={() => changeState(GAME_STATE.PLAY)} >开始游戏</button> }
return null; }
function Page(){ const state = useRef(GAME_STATE.INIT);
const changeState = useCallback((newState) => { state.current = newState; }, [state]);
return (<div> // ....other code <RenderButton state={state.current} changeState={changeState} /> </div>); }
|
权限控制
设计一个权限控制,用于不同角色对网站文章的权限分配。
1 2 3 4 5 6 7 8 9 10 11 12
| export enum ARTICLE_RULE{ VIEW = 1 << 0, EDIT = 1 << 1, PUBLISH = 1 << 2, DELETE = 1 << 3, }
export enum ROLE{ GUEST = ARTICLE_RULE.VIEW, ADMIN = ARTICLE_RULE.VIEW | ARTICLE_RULE.EDIT | ARTICLE_RULE.PUBLISH | ARTICLE_RULE.DELETE, OPERATOR = ARTICLE_RULE.VIEW | ARTICLE_RULE.EDIT | ARTICLE_RULE.PUBLISH, }
|
1 2 3 4 5 6 7 8
| if(user.role === 'admin'){ console.log("user拥有admin权限"); user.rule = ROLE.ADMIN; }
if((user.rule & ARTICLE_RULE.DELETE) === ARTICLE_RULE.DELETE){ console.log("user拥有删除文章权限"); }
|
判断奇偶数
奇数,最末尾1位,一定是1
。
所以将数字与1
(二进制表示为:00000000000000000000000000000001
)作位操作&
。
如果等于1
,则是奇数。
1 2 3
| function isOdd(number){ return number & 1 === 1; }
|
交换两个变量的值
1 2 3 4 5 6 7
| let a = 1; let b = 2;
a = a ^ b; b = a ^ b; a = a ^ b;
|
判断整数是否相等
1 2 3
| function isEqual(number1, number2){ return (number1 ^ number2) === 0; }
|
判断是否为负数
如果是负数,则对数字进行 无符号右移
位操作,会变成一个新的数字。
所以,如果是负数,则两个数字不相等。
1 2 3
| function isMinus(number){ return number !== (number >>> 0); }
|
正浮点数取整
1 2 3
| function toInt(floatNumber){ return floatNumber >>> 0; }
|
正负浮点数取整
1 2 3
| function toInt(floatNumber){ return floatNumber | 0; }
|
1 2 3
| function toInt(floatNumber){ return ~~floatNumber; }
|
1 2 3
| function toInt(floatNumber){ return floatNumber >> 0; }
|
十进制转换成二进制
1 2 3
| function dec2bin(dec){ return (dec).toString(2); }
|
二进制转换成十进制
1 2 3
| function bin2dec(bin){ return parseInt(`${bin}`, 2) }
|
参考