2.5.2位操作符

         位操作符用于在最基本的层次上,即按内存中表示数值的位来操作数值。ECMAScript中所有数值都以IEEE-754 64位格式存储,但位操作符并不直接操作64位的值。而是先将64位的值转换成32位的整数,然后执行操作,最后再将结果转换为64位。对于开发人员来说,由于64位存储格式是透明的,因此整个过程就像是只存在32位的整数一样。

         对于有符号的整数,32位中的前31位用于表示整数的值。第32位用于表示数值的符号:0表示正数,1表示负数。这个表示符号的位叫做符号位,符号位的值决定了其他数值的格式。其中,正数以纯二进制格式存储,31位中的每一位都表示2的幂。没有用到的位以0填充,及忽略不计。例如,数值18的二进制表示是10010。这是5个有效位,这5位本身就决定了实际的值:

第 2 章 基本概念------------(5)_JavaScript位操作符

         负数同样以二进制存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要经过下列3个步骤:

(1)  求这个数值绝对值的二进制码(例如,要求-18的二进制补码,先求18的二进制码);

(2)  求二进制反码,即将0替换为1,将1替换为0;

(3) 得到的二进制反码加1。

         根据这三个步骤求得-18的二进制码,首先就要求得18的二进制码,即:

         00000000 0000 0000 0000 0000 0001 0010

         然后,求其二进制反码,即0和1 互换:

         11111111 1111 1111 1111 1111 1110 1101

         最后,二进制反码加1:

         11111111 1111 1111 1111 1111 1110 1101

                                                                                  1

         ----------------------------------------------------------

         11111111 1111 1111 1111 1111 1110 1110

         这样,就求得了-18的二进制表示,即11111111 1111 1111 1111 1111 1110 1110。在处理有符号整数时,是不能访问为31的。

         在二进制字符串形式输出一个负数时,看到的只是在这个负数绝对值的二进制码前面加上一个符号,如下所示:        

var  num=-18;
alert(num.toString(2)); //"-10010"

         在ECMAScript中,当对数值应用位操作符时,后天会发生如下转换过程:64位的数值被转换成32位数值,然后执行位操作,最后再将32位的结果转换回64位数值。这个转换过程也导致一个严重的副效应,即在对特殊的NaN和Infinity值应用位操作时,这两个值都会被当成0来处理。

         如果对非数值应用位操作符,会先使用Number()函数将该值转换为一个数值(自动完成),然后再应用位操作。得到的结果将是一个数值。

2.5.2.1 按位非(NOT)

         按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。按位非是ECMAScript操作符中少说几个与二进制计算有关的操作符之一。例如:        

var num1 = 25;            // 二进制00000000000000000000000000011001
var num2 = ~num1; // 二进制11111111111111111111111111100110
alert(num2); // -26

       这也验证了按位非操作的本质:操作数的负值减1。

2.5.2.2 按位与(AND)

         按位与操作符由一个和号字符(&)表示,它有两个操作符数。从本质上讲,按位与操作就是将两个数值的每一位对齐,然后根据下表中的规则,对相同位置上的两个数执行AND操作:         第 2 章 基本概念------------(5)_JavaScript左移_02

         简而言之,按位与操作只在两个数值的对应为都是1时才返回1,任何一位是0,结果都是0。

2.5.2.3 按位或(OR)

         按位或操作符由一个竖线号(|)表示,同样也有两个操作数。按位或操作遵循下面真值表:

      第 2 章 基本概念------------(5)_JavaScript右移_03

         由此可见,按位或操作在有一个位是1的情况下就返回1,而只有两个位都是0的情况下才返回0。例如25与3按位或的结果是27:

25 = 0000 0000 0000 0000 0000 0000 0001 1001  3 = 0000 0000 0000 0000 0000 0000 0000 0011
--------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011
  

2.5.2.4 按位异或(XOR)

         按位异或操作符由一个插入符号(^)表示,也有两个操作数。以下是按位异或的真值表:

         第 2 章 基本概念------------(5)_JavaScript位操作符_04

         这个操作在两个数值对应位上只有一个1时才返回1,如果对应的两位都是1或都是0,则返回0。

         25与3按位异或的结果是26:

 25 = 0000 0000 0000 0000 0000 0000 0001 1001
    3 = 0000 0000 0000 0000 0000 0000 0000 0011
           ---------------------------------------------
     XOR = 0000 0000 0000 0000 0000 0000 0001 1010
    

2.5.2.5 左移(<<)

         左移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。例如,如果将数值2(二进制码为10)向左移动5位,结果就是64(1000000),代码如下所示:       

var oldValue = 2;                        // 等于二进制的10
var newValue = oldValue << 5; // 等于二进制的1000000,十进制的64

       注意,向左移位后,原数值的右侧多出了5个空位。左移操作会以0来填充这些空位,以便得到的结果是一个完整的32位二进制数。

     第 2 章 基本概念------------(5)_JavaScript位与或非_05

       左移不会影响操作数的符号位。换句话说,如果将-2向左移动5位,结果位将是-64,而非64。

2.5.2.6 右移(>>)

         有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位。有符号位的右移操作与左移操作恰好相反,即如果将64向右移动5位结果将变回2:        

var oldValue = 64;                             // 等于二进制的1000000
var newValue = oldValue >> 5; // 等于二进制的10,即十进制的2

       同样,在移位过程中,原数值也会出现空位。只不过这次的空位出现在原数值的左侧、符号位的右侧。

      第 2 章 基本概念------------(5)_JavaScript左移_06

2.5.2.7 无符号右移(>>>)

         无符号右移操作符由3个大于号(>>>)表示,这个操作符会将数值的所有32位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。如果将64无符号右移5位,结果仍然还是2:        

var oldValue = 64;                             // 等于二进制的1000000
var newValue = oldValue >>> 5; // 等于二进制的10,即十进制的2

       但是对负数来说,情况就不一样了。首先,无符号右移是以0 来填充空位,而不是像有符号右移那样以符号位的值来填充空位。所以,对正数的无符号右移与有符号右移结果相同,但对负数的结果就不一样了。其次,无符号右移操作符会把负数的二进制码当成正数的二进制码。而且,由于负数以其绝对值的二进制补码形式表示,因此就会导致无符号右移后的结果非常之大,如下面的例子所示:       

var oldValue = -64;                     // 等于二进制的11111111111111111111111111000000
var newValue = oldValue >>> 5; // 等于十进制的134217726

         这里,当对64 执行无符号右移5 位的操作后,得到的结果是134217726。之所以结果如此之大,是因为64 的二进制码为11111111111111111111111111000000,而且无符号右移操作会把这个二进制码当成正数的二进制码,换算成十进制就是4294967232。如果把这个值右移5 位,结果就变成了00000111111111111111111111111110,即十进制的134217726。