本文深入剖析JavaScript位运算符的底层原理,重点解释JS仅支持32位位操作的机制及IEEE754存储差异。通过负数无符号右移、游戏状态机流转及权限分配等实战案例,展示位运算在奇偶判断、变量交换及浮点取整中的具体应用,帮助开发者掌握高效编程技巧。
概览
本文将系统解析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 |
位操作支持多少位?
JavaScript仅支持对32位二进制数进行位操作,这意味着其能处理的十进制最大数值为 4294967295 。
parseInt('11111111111111111111111111111111', 2); // 4294967295下面验证超过32位二进制数进行位操作时的表现:
// 33位二进制数,得到十进制数字 8589934591
parseInt('111111111111111111111111111111111', 2); // 8589934591
// 对数字进行无符号位右移
8589934591 >>> 0; // 4294967295
4294967295 >>> 0; // 4294967295观察可知,对数字 8589934591 和 4294967295 执行 无符号位右移0位 操作后,所得结果完全一致。
造成该现象的根本原因在于,JavaScript的位操作实现机制仅支持 32位 位宽。
注意:
ECMAScript规范中所有数值在内存里均以IEEE75464位格式存储,但在执行位操作时,必须先将数值转换为32位格式进行处理。
负数的无符号右移
为什么 -2 >>> 1 的输出结果是 2147483647?
在位运算过程中,-2 采用补码形式表示,即其二进制补码为 1 1111111111111111111111111111110。
其中,最高位(第1位)为符号位。当符号位 1 为1时,代表当前数值为负数。
将 -2 进行无符号右移1位操作,最右侧的 0 被移出丢弃,剩余31位 1111111111111111111111111111111 向左移动,随后在左侧高位补入 0,最终得到 01111111111111111111111111111111。
结果 01111111111111111111111111111111 对应的十进制数值为 2147483647。
状态控制
典型应用场景:
以下示例结合 React 与 TypeScript,利用位运算实现游戏状态机的状态流转,并根据当前状态渲染对应的操作按钮。
// 游戏状态定义
export enum GAME_STATE{
INIT = 1 << 0, // 二进制表示:00001
JOIN = 1 << 1, // 二进制表示:00010
PREPARE = 1 << 2, // 二进制表示:00100
PLAY = 1 << 3, // 二进制表示:01000
}// 根据状态渲染按钮
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>);
}权限控制
设计一套权限控制方案,用于实现不同角色对网站文章的不同访问权限分配。
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, // 运营
}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(其二进制表示为:00000000000000000000000000000001)进行位操作 &。
若运算结果等于 1,则判定该数字为奇数。
function isOdd(number){
return number & 1 === 1;
}交换两个变量的值
let a = 1;
let b = 2;
a = a ^ b; // 这一步,a缓存了 a ^ b 的结果
b = a ^ b; // 等价为: b = a ^ b ^ b, 其中 b ^ b = 0; 所以 b = a ^ 0 = a
a = a ^ b; // 同上判断整数是否相等
function isEqual(number1, number2){
return (number1 ^ number2) === 0;
}判断是否为负数
若数字为负数,则对其进行 无符号右移 位操作后,会生成一个新的数值。
因此,如果原数字与经过无符号右移后的数字不相等,则说明原数字为负数。
function isMinus(number){
return number !== (number >>> 0);
}正浮点数取整
function toInt(floatNumber){
return floatNumber >>> 0;
}正负浮点数取整
function toInt(floatNumber){
return floatNumber | 0;
}function toInt(floatNumber){
return ~~floatNumber;
}function toInt(floatNumber){
return floatNumber >> 0;
}十进制转换成二进制
function dec2bin(dec){
return (dec).toString(2);
}二进制转换成十进制
function bin2dec(bin){
return parseInt(`${bin}`, 2)
}参考
- 聊聊JavaScript中的7种位运算符,看看在实战中如何妙用?
- 负数的二进制表示方法(正数:原码、负数:补码)
到此这篇关于JavaScript位运算符的文章就介绍到这了,更多相关JS位运算符内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!