|
3.6 ECMAScript位运算符及表达式 |
在ECMAScript语法中,还定义了位运算符用于对数字底层(即表示数字的32位二进制数)进行操作。学习位运算符,我们一定要先弄明白二进制编码的原理,这一点非常重要。另外,如果读者学习过《汇编语言程序设计》或者《计算机组成原理》这两门课程,学习位运算符自然就不会有什么难度。下面对这些位运算符逐一进行介绍。
位运算符用于32位二进制数的位操作,且操作结果均转换为十进制的JavaScript数字。关于ECMAScript语法中定义的位运算符的内容详见表3-6。
表3-6 ECMAScript位运算符与表达式
在计算机操作系统中,一般对整数的编码有两种形式,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数)。而在ECMAScript语法规范中,所有整数默认都是有符号的整数。
对于有符号的整数(32位二进制编码),一般使用前31位表示整数的数值,而专用第32位表示整数的符号,具体就是0表示正数,1表示负数。因此有符号整数的数值范围是在-2147483648~2147483647之间。比如数字15的二进制可以使用图3.20来表示。
图3.20 ECMAScript整数编码表示方法
前31位中的每一位都表示数值2的幂。第1位(位0)开始表示(2 0 ),第2位(位1)表示(2 1 ),第3位(位2)表示(2 2 ),第4位(位3)表示(2 3 ),这样依次加则是(2 0 + 2 1 +2 2 + 2 3 = 15)。而从第5位(位4)开始没用到的位则用0填充。最后,第32位(位31)为0,则表示为正数。
下面,来看一个应用正整数编码的代码示例(详见源代码ch03目录中ch03-js-operator-encode-posi-num.html文件)。
【代码3-18】
01 <script type="text/javascript"> 02 var iNum = 15; 03 console.log("15 to binary is " + iNum.toString(2)); 04 </script>
关于【代码3-18】的分析如下:
这段代码主要就是将数值15转换为二进制数,并进行了输出操作。
页面效果如图3.21所示。从第03行代码输出的结果来看,仅输出了二进制“1111”,而不是全部32位二进制数,这是因为填充位“0”被省略掉了,这是符合语法规则的。
图3.21 ECMAScript位运算符(正数编码)
而对于负整数也是存储为二进制代码的,不过采用的形式是二进制补码形式。关于二进制补码的内容,读者可以参考前面提到的《汇编语言程序设计》或者《计算机组成原理》方面的教材,这里就不深入讨论了。
不过,之所以不对二进制补码进行详细介绍,是因为ECMAScript语法定义中对负整数采用简单的处理方式,即直接使用负号(-)来表示将负整数转换为二进制数的形式。我们还是通过具体代码示例来介绍。
下面,来看一个应用负整数编码的代码示例(详见源代码ch03目录中ch03-js-operator-encode-nega-num.html文件)。
【代码3-19】
01 <script type="text/javascript"> 02 var iNum = -15; 03 console.log("-15 to binary is " + iNum.toString(2)); 04 </script>
关于【代码3-19】的分析如下:
这段代码主要就是将负数(-15)转换为二进制数,并进行输出操作。
页面效果如图3.22所示。从第03行代码输出的结果来看,负数直接输出了二进制“-1111”,而不是二进制的补码形式,这是因为ECMAScript语法进行了简化处理,也是为了更方便设计人员进行设计开发。
图3.22 ECMAScript位运算符(负数编码)
最后,再介绍一下无符号整数的处理方式。无符号整数是把最后一位(位31)作为一个具体数位来处理,所以无符号整数的第32位不表示数字的符号,而是具体的数值。因此无符号整数的数值范围为从0~4294967295。
ECMAScript语法中规定所有整数都默认存储为有符号的整数。仅在使用ECMAScript的位运算符操作时,才会创建出无符号整数。
在ECMAScript语法定义中,位运算符(NOT)是用否定号(~)来表示的,主要用于二进制算术运算。一般来说,位运算符(NOT)的操作步骤如下:
第一步,先将运算数转换成32位二进制数;
第二步,再将第一步得到的二进制数转换成其自身的二进制数的反码形式;
第三步,最后再将二进制数反码转换成浮点数。
二进制反码的内容与二进制补码类似,读者可自行参考前文中提到的相关课程书籍。即使不去详细了解二进制反码的内容,也不会影响对位运算符(NOT)的使用,因为ECMAScript语法仍会默认输出十进制数值。
下面,来看一个NOT运算符表达式的代码示例(详见源代码ch03目录中ch03-js-operator-not.html文件)。
【代码3-20】
01 <script type="text/javascript"> 02 var iNum = 255; 03 console.log("255 to binary is " + iNum.toString(2)); 04 var iNum_NOT = ~iNum; 05 console.log("NOT 255 to binary is " + iNum_NOT.toString(2)); 06 console.log("NOT 255 is " + iNum_NOT.toString()); 07 </script>
关于【代码3-20】的分析如下:
这段代码主要就是对数值255使用NOT运算符进行位操作运算。
第04行代码中通过NOT运算符(~)对第02行代码中定义的变量(iNum)进行了位操作运算(~iNum)。
页面效果如图3.23所示。从第03行代码输出的结果来看,数值255的二进制数形式为11111111;从第05行代码输出的结果来看,对数值255进行NOT位运算符操作后,返回结果为二进制数-100000000;从第06行代码输出的结果来看,返回的结果正好是对数值255先求负(二进制反码),然后减1的结果。
图3.23 ECMAScript位运算符(NOT)
在ECMAScript语法定义中,位运算符(AND)是由和号(&)来表示的,主要用于二进制算术运算。一般来说,使用位运算符(AND)时先要把两个运算数按照位对齐,然后根据表3-7中的规则进行AND运算。
表3-7 ECMAScript位运算符(AND)规则表
下面,来看一个AND运算符表达式的代码示例(详见源代码ch03目录中ch03-js-operator-and.html文件)。
【代码3-21】
01 <script type="text/javascript"> 02 var iNum1 = 255; 03 console.log("255 to binary is " + iNum1.toString(2)); 04 var iNum2 = 0xAA; 05 console.log("0xAA to binary is " + iNum2.toString(2)); 06 var iNum_AND = iNum1 & iNum2; 07 console.log("255 & 0xAA to binary is " + iNum_AND.toString(2)); 08 console.log("255 & 0xAA to hex is " + iNum_AND.toString(16)); 09 console.log("255 & 0xAA is " + iNum_AND.toString()); 10 </script>
关于【代码3-21】的分析如下:
这段代码主要就是对数值255和0xAA使用AND运算符进行位操作运算。
页面效果如图3.24所示。从第03行代码输出的结果来看,数值255的二进制数形式为11111111;从第05行代码输出的结果来看,十六进制数值0xAA的二进制数形式为10101010;从第07行代码输出的结果来看,数值255和0xAA通过AND位运算后,运算结果的二进制形式仍为10101010,该结果符合表3-7中的运算规则;从第08行代码输出的结果来看,值“aa”正是十六进制数0xAA;从第09行代码输出的结果来看,十六进制数0xAA对应的十进制数正是数值170。
图3.24 ECMAScript位运算符(AND)
在ECMAScript语法定义中,位运算符(OR)是由符号(|)来表示的,主要用于二进制算术运算。一般来说,使用位运算符(OR)时同样要把两个运算数按照位对齐,然后根据表3-8中的规则进行OR运算。
表3-8 ECMAScript位运算符(OR)规则表
下面,来看一个OR运算符表达式的代码示例(详见源代码ch03目录中ch03-js-operator-or.html文件),这段代码是在【代码3-21】的基础上稍加修改而完成的,读者可以进行参考对比。
【代码3-22】
01 <script type="text/javascript"> 02 var iNum1 = 255; 03 console.log("255 to binary is " + iNum1.toString(2)); 04 var iNum2 = 0xAA; 05 console.log("0xAA to binary is " + iNum2.toString(2)); 06 var iNum_OR = iNum1 | iNum2; 07 console.log("255 | 0xAA to binary is " + iNum_OR.toString(2)); 08 console.log("255 | 0xAA to hex is " + iNum_OR.toString(16)); 09 console.log("255 | 0xAA is " + iNum_OR.toString()); 10 </script>
关于【代码3-22】的分析如下:
这段代码主要就是对数值255和0xAA使用OR运算符进行位操作运算。
页面效果如图3.25所示。
图3.25 ECMAScript位运算符(OR)
从第03行代码输出的结果来看,数值255的二进制数形式为11111111;从第05行代码输出的结果来看,十六进制数值0xAA的二进制数形式为10101010;
从第07行代码输出的结果来看,数值255和0xAA通过OR位运算后,运算结果的二进制形式仍为11111111,该结果符合表3-8中的运算规则;
从第08行代码输出的结果来看,值“ff”正是十六进制数0xFF;
从第09行代码输出的结果来看,十六进制数0xFF对应的十进制数正是数值255。
在ECMAScript语法定义中,位运算符(XOR)是用符号(^)来表示,主要用于二进制算术运算。一般来说,使用位运算符(XOR)时同样要把两个运算数按照位对齐,然后根据表3-9中的规则进行XOR运算。
表3-9 ECMAScript位运算符(XOR)规则表
下面,来看一个XOR运算符表达式的代码示例(详见源代码ch03目录中ch03-js-operator-xor.html文件),这段代码同样是在【代码3-21】和【代码3-22】的基础上稍加修改而完成的,读者可以进行参考对比。
【代码3-23】
01 <script type="text/javascript"> 02 var iNum1 = 255; 03 console.log("255 to binary is " + iNum1.toString(2)); 04 var iNum2 = 0xAA; 05 console.log("0xAA to binary is " + iNum2.toString(2)); 06 var iNum_XOR = iNum1 ^ iNum2; 07 console.log("255 ^ 0xAA to binary is " + iNum_XOR.toString(2)); 08 console.log("255 ^ 0xAA to hex is " + iNum_XOR.toString(16)); 09 console.log("255 ^ 0xAA is " + iNum_XOR.toString()); 10 </script>
关于【代码3-23】的分析如下:
这段代码主要就是对数值255和0xAA使用XOR运算符进行位操作运算。
页面效果如图3.26所示。
图3.26 ECMAScript位运算符(XOR)
从第03行代码输出的结果来看,数值255的二进制数形式为11111111;从第05行代码输出的结果来看,十六进制数值0xAA的二进制数形式为10101010;
从第07行代码输出的结果来看,数值255和0xAA通过XOR位运算后,运算结果的二进制形式为1010101,写成8位二进制就是01010101,该结果符合表3-9中的运算规则;
从第08行代码输出的结果来看,值“55”正是十六进制数0x55;
从第09行代码输出的结果来看,十六进制数0x55对应的十进制数正是数值85。
在ECMAScript语法定义中,左移位运算符是用符号(<<)来表示的,主要用于实现将数值中的所有数位向左移动指定数量的位数的二进制算术运算。
在左移运算时数字右边可能会多出若干个空位,此时会使用数字0来填充这些空位,保证结果为完整的32位二进制数。
左移运算会保留数字的符号位(第32位)。不过设计人员不用担心,一般是不能直接访问第32位符号位的,一切都是通过ECMAScript语法在后台来实现的。
下面,来看一个左移运算符(<<)表达式的代码示例(详见源代码ch03目录中ch03-js-operator-shl.html文件)。
【代码3-24】
01 <script type="text/javascript"> 02 var iNum = 255; 03 console.log("255 to binary is " + iNum.toString(2)); 04 var iNum_shl_2 = iNum << 2; 05 console.log("255 << 2 to binary is " + iNum_shl_2.toString(2)); 06 console.log("255 << 2 is " + iNum_shl_2.toString()); 07 var iNum_shl_8 = iNum << 8; 08 console.log("255 << 8 to binary is " + iNum_shl_8.toString(2)); 09 console.log("255 << 8 is " + iNum_shl_8.toString()); 10 var iNum_shl_16 = iNum << 16; 11 console.log("255 << 16 to binary is " + iNum_shl_16.toString(2)); 12 console.log("255 << 16 is " + iNum_shl_16.toString()); 13 var iNum_shl_32 = iNum << 32; 14 console.log("255 << 32 to binary is " + iNum_shl_32.toString(2)); 15 console.log("255 << 32 is " + iNum_shl_32.toString()); 16 </script>
关于【代码3-24】的分析如下:
这段代码主要就是对数值255使用左移运算符(<<)进行位操作运算。
页面效果如图3.27所示。
图3.27 ECMAScript位运算符(左移)
从第03行代码输出的结果来看,数值255的二进制数形式为11111111;
从第05~06行代码输出的结果来看,对数值255左移两位后的结果为1111111100,右边空位自动补0;
从第08~09行代码输出的结果来看,对数值255左移8位后的结果为1111111100000000,右边空位同样自动补0;
从第11~12行代码输出的结果来看,对数值255左移16位后的结果为111111110000000000000000,右边空位依然自动补0;
从第14~15行代码输出的结果来看,对数值255左移32位后的结果为11111111,说明左移32位的操作结果仍为初始值。
在ECMAScript语法定义中,保留符号位的右移运算符是用符号(>>)来表示的,主要用于实现将数字中的所有数位向右移动指定数量的位数,且保留该数的符号(正号或负号)位的二进制算术运算。
在右移运算时数字左边可能会多出若干个空位,此时会使用数字0来填充这些空位,保证结果为完整的32位二进制数。
下面,来看一个保留符号位的右移运算符(>>)表达式的代码示例(详见源代码ch03目录中ch03-js-operator-shr.html文件)。
【代码3-25】
01 <script type="text/javascript"> 02 var iNum = 2139095040; 03 console.log("2139095040 to binary is " + iNum.toString(2)); 04 var iNum_shr_2 = iNum >> 2; 05 console.log("2139095040 >> 2 to binary is " + iNum_shr_2.toString(2)); 06 console.log("2139095040 >> 2 is " + iNum_shr_2.toString()); 07 var iNum_shr_8 = iNum >> 8; 08 console.log("2139095040 >> 8 to binary is " + iNum_shr_8.toString(2)); 09 console.log("2139095040 >> 8 is " + iNum_shr_8.toString()); 10 var iNum_shr_16 = iNum >> 16; 11 console.log("2139095040 >> 16 to binary is " + iNum_shr_16.toString(2)); 12 console.log("2139095040 >> 16 is " + iNum_shr_16.toString()); 13 var iNum_shr_32 = iNum << 32; 14 console.log("2139095040 >> 32 to binary is " + iNum_shr_32.toString(2)); 15 console.log("2139095040 >> 32 is " + iNum_shr_32.toString()); 16 </script>
关于【代码3-25】的分析如下:
这段代码主要就是对数值2139095040使用右移运算符(>>)进行保留符号位的位操作运算。
页面效果如图3.28所示。
图3.28 ECMAScript位运算符(保留符号位的右移)
从第03行代码输出的结果来看,数值2139095040的二进制数形式为1111111100000000000000000000000;
从第05~06行代码输出的结果来看,对数值2139095040右移两位后的结果为11111111000000000000000000000;
从第08~09行代码输出的结果来看,对数值2139095040右移8位后的结果为11111111000000000000000;
从第11~12行代码输出的结果来看,对数值2139095040右移16位后的结果为111111110000000;
从第14~15行代码输出的结果来看,对数值2139095040右移32位后的结果仍为2139095040,说明保留符号位的右移32位的操作结果仍为初始值。
在ECMAScript语法定义中,无符号位的右移运算符是用符号(>>>)来表示的,主要用于实现将数字中的所有数位(包括第32位的符号位)整体向右移动指定数量的位数的二进制算术运算。
对于正数的无符号位右移运算,其结果与保留符号位的右移运算是一致的。但对于负数来说,无符号位的右移运算与保留符号位的右移运算所返回的结果就完全不一致了。
下面,来看一个无符号位的右移运算符(>>>)表达式的代码示例(详见源代码ch03目录中ch03-js-operator-shr-nosign.html文件)。
【代码3-26】
01 <script type="text/javascript"> 02 var iNum = 256; 03 console.log("256 to binary is " + iNum.toString(2)); 04 var iNum_shr = iNum >> 8; 05 console.log("256 >> 8 to binary is " + iNum_shr.toString(2)); 06 console.log("256 >> 8 is " + iNum_shr.toString()); 07 var iNum_shr_nosign = iNum >>> 8; 08 console.log("256 >>> 8 to binary is " + iNum_shr_nosign.toString(2)); 09 console.log("256 >>> 8 is " + iNum_shr_nosign.toString()); 10 var iNum_minus = -256; 11 console.log("-256 to binary is " + iNum_minus.toString(2)); 12 var iNum_shr_minus = iNum_minus >> 8; 13 console.log("-256 >> 8 to binary is " + iNum_shr_minus.toString(2)); 14 console.log("-256 >> 8 is " + iNum_shr_minus.toString()); 15 var iNum_shr_nosign_minus = iNum_minus >>> 8; 16 console.log("-256 >>> 8 to binary is " + iNum_shr_nosign_minus.toString(2)); 17 console.log("-256 >>> 8 is " + iNum_shr_nosign_minus.toString()); 18 </script>
关于【代码3-26】的分析如下:
这段代码主要是对正数256和负数-256同时分别使用保留符号位的右移运算符(>>)和无符号位的右移运算符(>>>)进行了位操作运算,目的就是验证无符号位的右移运算符(>>>)对于正数和负数会返回不同的操作结果。
页面效果如图3.29所示。
图3.29 ECMAScript位运算符(无符号位的右移)
从第03行代码输出的结果来看,数值256的二进制数形式为100000000;
从第04~06行代码输出的结果来看,对数值256使用保留符号位的位运算符(>>)右移8位后的结果为1;
从第07~09行代码输出的结果来看,对数值256使用无符号位的位运算符(>>>)右移8位后的结果仍为1;
这就说明对于正数,保留符号位的右移运算符(>>)和无符号位的右移运算符(>>>)操作的结果是一致的。
从第11行代码输出的结果来看,数值-256的二进制数形式为-100000000;
从第12~14行代码输出的结果来看,对数值-256使用保留符号位的位运算符(>>)右移8位后的结果为-1;
而从第15~17行代码输出的结果来看,对数值-256使用无符号位的位运算符(>>>)右移8位后的结果为16777215。
这就说明对于负数,保留符号位的右移运算符(>>)和无符号位的右移运算符(>>>)操作的结果是不同的,因为无符号位的右移运算会将符号位的数值1一起右移,所以操作后数值结果会发生变化。