5.2 位运算符
JavaScript定义了7个位运算符,它们可以分为两类。
逻辑位运算符:位与(&)、位或(|)、位异或(^)和位非(~)。
移位运算符:左移(<<)、右移(>>)和无符号右移(>>>)。
5.2.1 位运算概述
位运算就是对二进制数进行的计算。
位运算是整数的逐位运算。例如,1+1=2,这在十进制计算中是正确的;但是在二进制计算中,1+1= 10;而对于二进制数100取反,则就等于001,而不是-100。
在JavaScript中,位运算要求运算数必须是32位的整数,如果位运算数是非整型,或者大于32位的整数,则将返回NaN。
在默认情况下,所有整数都是带有符号的整数。带有符号的整数中,第1~31位表示整数的值,第32位表示整数的符号,0表示正数,1表示负数。因此整数值的范围为-2147483648~2147483647。
对于正整数来说,它以二进制形式进行存储,第1~31位中每一位都表示2的幂,序数从0开始。第1位表示20,第2位表示21,第3位表示22,依此类推。对于没有用到的位使用0来进行填充。例如,对于十进制数值12,使用二进制表示法如图5-3所示。
图5-3 12的二进制表示法
其中第1~4位为有效位,如果使用如下方法返回对应的二进制值,可以看到返回值仅是有效位,其他数位由于并不重要,因此就没有返回。
alert((12).toString(2)); //返回二进制值1100
实际上利用有效位,对于二进制值1100,可以很轻松地使用如下方法转换为十进制值:
(23*1)+(22*1)+(21*0)+(20*0) =8 + 4 + 0 + 0 = 12
对于负数来说,二进制的存储方式一般采用二进制补码的方式来实现:
第1步,先计算负数对应的正数的二进制值。例如,对于-12,则先计算12的二进制值,如图5-3所示。
第2步,对第一步中计算的二进制值进行反码。所谓反码就是把位值逐位取反。例如,0取反为1,1取反为0。针对图5-3所示的12的二进制值反码之后,则如图5-4所示。
图5-4 12的二进制值反码
第3步,为二进制反码值加1,如图5-5所示。
图5-5 为二进制值反码加1
第4步,最终,-12的二进制值为:11111111111111111111111111110100。
但是如果使用如下方法返回-12的二进制值:
alert((-12).toString(2)); //返回值-1100
则返回值不是11111111111111111111111111110100,而是-1100,即使用数字绝对值的二进制值加上负号的形式返回。
在处理带有符号的整数时,JavaScript是不允许访问第32位符号位的值。
对于无符号的整数来说,第32位不再是符号位,而是用来表示值,所以它的数值范围为0~4294967295。如果数值小于等于214783647的整数来说,无符号整数与有符号整数表示是一样的,而大于214783647的整数,则第32位为1,此时与有符号整数表示是不一样的。当然,JavaScript所有数值默认都是带有符号位的,只有数值参与到位运算之后,才能生成无符号的整数。
5.2.2 逻辑位运算
逻辑位运算符与逻辑运算符的运算方式相同,但是针对的对象不同。逻辑位运算符针对的是二进制的整数值,而逻辑运算符针对的是非二进制的其他类型数据。
&运算符(位与)表示布尔AND操作。它对二进制值逐位进行比较,并根据表5-2所示的换算表返回结果。
表5-2 &运算符
【示例1】12和5进行位与运算,则返回值为4。
alert(12&5); //返回值4
如图5-6所示以算式的形式解析12和5进行位与运算的过程。通过位与运算,只有第3位的值为全true,故返回true,其他位均返回false。
图5-6 12和5进行位与运算
提示:在位运算中数值1表示true,0表示false,反之亦然。
|运算符(位或)表示布尔OR操作。位或运算将根据表5-3所示的换算表返回结果。
表5-3
【示例2】12和5进行位或运算,则返回值为13。
alert(12|5); //返回值13
如图5-7所示以算式的形式解析12和5进行位或运算的过程。通过位或运算,只有第2位的值为false外,其他位均返回true。
运算符
^运算符(位异或)表示布尔XOR操作。位异或运算将根据表5-4所示的换算表返回结果。
图5-7 12和5进行位或运算
【示例3】12和5进行位异或运算,则返回值为9。
alert(12^5); //返回值9
如图5-8所示以算式的形式解析12和5进行位异或运算的过程。通过位异或运算,第1、4位的值为true,而第2、4位的值为false。
表5-4 ^运算符
~运算符(位非)表示布尔NOT操作。位非运算将根据3步操作进行处理:
第1步,把运算数转换为32位的二进制整数。
第2步,逐位反码,即逐位进行取反操作。
第3步,把二进制反码转换为十进制浮点数。
【示例4】对12进行位非运算,则返回值为-13。
alert(~12); //返回值-13
如图5-9所示以算式的形式解析对12进行位非运算的过程。
图5-8 12和5进行位异或运算
位非运算本质上就是对数字进行求负运算,然后再减1。因此,使用如下方法也可以获得相同的结果。
~12 = -12-1;
如果使用如下语句则可以检测它们是否相等:
alert(~12==-12-1); //返回true
5.2.3 移位运算
移位运算就是对二进制值进行有规律移位,移位运算可以设计很多奇妙的效果,这在图形图像编程中应用比较广泛。
【示例1】把数字5向左移动2位,则返回值为20。
alert(5<<2); //返回值20
如果用算式图进行演示则如图5-10所示。
图5-9 对12进行位非运算
在移位运算过程中,右侧空出的位置,JavaScript自动使用0进行填充,确保整个值是一个完整的32位二进制值。在左移操作中,符号位始终保持不变。在移位过程中,如果超出32位的值将自动被丢弃。
右移运算包含有符号和无符号两种形式。有符号右移运算与左移运算操作相反,它把32位数字中的所有有效位整体右移。移位后的空位位于有效位的左侧,但是位于符号位的右侧,此时JavaScript会使用符号位的值来填充空位。移动过程中超出的值将被自动丢弃。
【示例2】把数值1000向右移8位,则返回值为3。
alert(1000>>8); //返回值3
如果用算式图进行演示则如图5-11所示。
图5-10 把5向左移位2位运算
【示例3】把数值-1000向右移8位,则返回值为-4。
alert(-1000>>8); //返回值-4
如果用算式图进行演示则如图5-12所示。可以看到当符号位值为1时,则有效位左侧的空位全部使用1进行填充。
图5-11 把1000向右移位8位运算
对于无符号右移,可以使用>>>运算符来表示,它将无符号的32位整数所有数位整体右移。对于无符号或正数右移运算,则无符号右移与有符号右移运算的结果是相同的。
图5-12 把-1000向右移位8位运算
【示例4】下面两行表达式的返回值都是相同的。
alert(1000>>8); //返回值3 alert(1000>>>8); //返回值3
【示例5】对于负数来说,无符号右移将使用0来填充所有的空位,同时会把负数看作正数来处理。所以所得的结果一般都会非常大。
alert(-1000>>8); //返回值-4 alert(-1000>>>8); //返回值16777212
如果用算式图进行演示则如图5-13所示。左侧的空位就不再用符号位的值来填充,而是用0来填充。
图5-13把-1000向右无符号移位8位运算
在执行运算时,JavaScript会把这个数值转换为无符号的等价形式,尽管第32位是一个符号位,但是此时会把它看作一个数值位进行运算。所以,在使用无符号右移运算符时,要特别小心,避免出现意想不到的错误。