本文深入解析JavaScript中的位运算机制,涵盖按位与(&)、或(|)、非(~)、异或(^)及左移、右移操作。通过具体实例展示如何利用位运算高效判断奇偶数、对浮点数向下取整、交换变量值以及计算2的n次方,同时辨析有符号与无符号右移在正负数处理上的差异,帮助开发者理解底层二进制操作以提升代码执行效率。
位运算基础与效率优势
在常规编程实践中,开发者较少直接使用位运算,若未深入研习,其原理往往难以直观理解。日常数值计算通常需先将数据转换为二进制形式再执行操作,而位运算则直接作用于二进制位,因此其执行效率通常更高。下文将通过具体实例剖析位运算的实际应用,以加深理解。
按位与(&):奇偶判断
按位与(&)
对于&&运算符,其逻辑为仅当两个操作数均为真时结果才为真。按位与(&)遵循相同逻辑:仅当两个对应二进制位均为1时,结果位才为1。例如,对数值1和3执行按位与操作的过程如下:
0001 & 0011 --------- 0001
只有当对应位均为1时结果位才为1,其余情况均为0。判断整数奇偶性时,常规做法是求余数:
function assert(n) {
if (n % 2 === 1) {
console.log("n是奇数");
} else {
console.log("n是偶数");
}
}
assert(3); // "n是奇数"
我们亦可利用数值与1进行按位与(&)操作来判断奇偶,该方法执行速度更快:
function assert(n) {
if (n & 1) {
console.log("n是奇数");
} else {
console.log("n是偶数");
}
}
assert(3); // "n是奇数"
具体位运算过程如下:
1 = 0001 3 = 0011 -------- & = 0001
奇数的二进制表示中,最低位(最后一位)必定为1;而数值1的二进制仅最低位为1,其余为0。因此,奇数与1进行按位与操作后,结果最低位仍为1,其他位为0。相反,偶数的二进制最低位为0,与1进行按位与操作后,所有位结果均为0。
按位或(|):浮点数取整
按位或(|)
按位或(|)与逻辑或(||)原理相似:只要两个操作数中有一个对应位为1,结果位即为1,否则为0。
0001 | 0011 --------- 0011
对浮点数执行向下取整时,常规方法如下:
var num = Math.floor(1.1); // 1
利用位运算同样可实现向下取整:
var num = 1.1 | 0; // 1
JavaScript中的位运算不支持浮点数,因此执行1.1 | 0时,系统会先将1.1转换为整数1,再执行位运算,效果等同于向下取整。故1 | 0的结果为1。
按位非(~):符号位与负数转换
按位非(~)
按位非(~)用于求二进制的反码:
var num = 1; // 二进制 00000000000000000000000000000001 var num1 = ~num; // 二进制 11111111111111111111111111111110
JavaScript中的数字默认采用有符号32位二进制表示,最高位(第32位)为符号位,1代表负数,0代表正数。那么二进制11111111111111111111111111111110对应的十进制值是多少?由于最高位为1,该数为负数。负数二进制转十进制的规则为:符号位保持不变,其余位取反后加1。对上述二进制码取反得到10000000000000000000000000000001,加1后得到10000000000000000000000000000010,其十进制值为-2。
按位异或(^):变量交换技巧
按位异或(^)
按位异或(^)的规则是:仅当两个对应位中有一个为1时,结果位才为1;若两个位相同(均为0或均为1),则结果位为0。
0001 ^ 0011 --------- 0010
任意数值与自身进行按位异或操作,结果必为0。这是因为每一位都与自身相同,根据异或规则,相同位运算结果为0。
在实际开发中,常需交换两个变量的值:
var num1 = 1, num2 = 2, temp;
temp = num1;
num1 = num2; // 2
num2 = temp; // 1
若追求代码简洁,可采用数组解构方式:
var num1 = 1, num2 = 2;
num1 = [num2, num2 = num1][0];
console.log(num1); // 2
console.log(num2); // 1
若希望进一步精简代码,可利用按位异或特性实现:
var num1 = 1, num2 = 2;
num1 ^= num2; // num1 = num1 ^ num2 = 1 ^ 2 = 3
num2 ^= num1; // num2 = num2 ^ (num1 ^ num2) = 2 ^ (1 ^ 2) = 1
num1 ^= num2; // num1 = num1 ^ num2 = 3 ^ 1 = 2
console.log(num1); // 2
console.log(num2); // 1
位移运算:左移与右移应用
有符号左移(<<)
有符号左移操作会将32位二进制数的所有位向左移动指定的位数,右侧空出的位补0。示例如下:
var num = 2; // 二进制10 num = num << 5; // 二进制1000000,十进制64
若需计算2的n次方,可利用左移运算:
function power(n) {
return 1 << n;
}
power(5); // 32
数值1的二进制表示为01(高位补零后),将其左移5位得到0100000,即十进制的32,这正是2的5次方。
有符号右移(>>)
有符号右移操作会将32位二进制数的所有位向右移动指定的位数,左侧空出的位由符号位填充。示例如下:
var num = 64; // 二进制1000000 num = num >> 5; // 二进制10,十进制2
利用右移运算可快速计算一个数的二分之一:
var num = 64 >> 1; // 32
有符号左移与右移操作均不会改变数值的符号位。
无符号右移:正负判断与边界处理
无符号右移(>>>)
对于正数,无符号右移与有符号右移的结果一致。但对于负数,无符号右移会将符号位一同右移,且系统会将负数的二进制码视为无符号正数处理:
var num = -64; // 11111111111111111111111111000000 num = num >>> 5; // 134217726
基于上述特性,可利用无符号右移判断数值的正负:
function isPos(n) {
return (n === (n >>> 0)) ? true : false;
}
isPos(-1); // false
isPos(1); // true
表达式-1 >>> 0虽未实际右移,但-1的二进制码(全1)被解释为正数二进制码:
11111111111111111111111111111111
因此,-1 >>> 0的结果为4294967295(即32位全1的无符号整数值)。
总结
上述实例在日常开发中较为常见且易于理解。对于复杂或晦涩的位运算,建议谨慎使用,以免增加代码阅读难度和维护成本。