1. 32位有符号整数
按位操作符(Bitwise operators)会使用内置函数,7.1.5 ToInt32 ( argument ),
先将其操作数转换成32位有符号整数,再进行位操作,最后返回一个32位有符号整数。
包括,
12.5.8 Bitwise NOT Operator ( ~ ),
12.9.3 The Left Shift Operator ( << ),
12.9.4 The Signed Right Shift Operator ( >> ),
12.12 Binary Bitwise Operators
因此,a | 0
,0 | a
,都可以将变量a
中数值转换为32位有符号整数。
某些特殊的值,并不是32位有符号整数的安全范围,它们会被转换为0
。
特殊值 | ToInt32 |
---|---|
-0 | 0 |
NaN | 0 |
Infinity | 0 |
-Infinity | 0 |
2. 补码编码
在计算机中表示有符号整数,通常使用补码(two's-complement)进行编码。
它将字的最高有效位解释为符号位,符号位被置为1
时,表示值为负,
符号位被置为0
时,表示值为非负。
因此,字长为4的二进制数0001
表示整数1
,其中0*2^3+0*2^2+0*2^1+1*2^0=1
,
而1111
就表示整数-1
,其中-1*2^3+1*2^2+1*2^1+1*2^0=-1
。
负数的补码,还可以按照“逐位取反后,加一”的方式来获取相应的整数值。
例如,1111
逐位取反0000
,然后再加一0001
,它是1
的二进制表示,
因此1111
就是表示-1
了。
3. ~x === -(x+1)
~
操作符,它首先将操作数转换成32位有符号整数,然后再按位取反。
例如,1
的32位补码编码为,
00000000 00000000 00000000 00000001
按位取反,
11111111 11111111 11111111 11111110
它表示什么呢?
先看最高为的符号位,是1
,它表示一个负数,
然后“逐位取反后,加一”,00000000 00000000 00000000 00000002
值为2
,
因此,11111111 11111111 11111111 11111110
表示-2
。
一般的,可以证明,
对于任意的32位有符号整数x
来说,~x === -(x+1)
。
详细证明见文后的附录。
4. 数组的索引范围
ECMAScript中,数组元素的索引范围是,0
到Math.pow(2,32)-2
。
规范9.4.2 Array Exotic Objects中指出,
Every Array object has a length property whose value is always a nonnegative integer less than 2^32.
因此,数组length
的最大值是Math.pow(2,32)-1
。
a = [];
a.length = Math.pow(2,32)-1;
a; // [empty × 4294967295]
a.length = Math.pow(2,32);
// Uncaught RangeError: Invalid array length
超过数组length
的索引,会被看做数组的属性值,
a = [];
a.length = Math.pow(2,32)-1;
// 最后一个元素的索引是Math.pow(2,32)-2
a; // [empty × 4294967295]
// 再为数组添加一个更大索引值的元素
a[Math.pow(2,32)-1] = 1;
a; // [empty × 4294967295, 4294967295: 1]
因此,indexOf
返回的最大值为Math.pow(2,32)-2
。
5. indexOf
Array.prototype.indexOf,
会返回给定数组元素在数组中的索引,如果找不到给定元素,就返回-1
。
因为只有~-1
等于0
,其他索引值取反都非0
,
所以,人们经常使用!~a.indexOf(element)
来判断元素是否在数组中。
这里有一个值得注意的事情,由于~
会首先将操作数转换成32位有符号整数,
所以,-1
和Math.pow(2,32)-1
具有相同的编码,
11111111 11111111 11111111 11111111
但是,数组的最大索引为Math.pow(2,32)-2
,小于上面这个值,
因此,对indexOf
返回的值进行取反,除了-1
之外,总是非0
值,是安全的做法。
附录
下面给出~x === -(x+1)
的证明。
(1)先看正整数
对于32位正整数来说,它的二进制编码为,
0nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn
其中n
表示0
或者1
,
则,~x
为,
1uuuuuuu uuuuuuuu uuuuuuuu uuuuuuuu
其中u
为n
的取反结果。
以上二进制表示,如果看成32位有符号整数,则由于符号位1
,它是一个负数,
其绝对值为,“逐位取反后,加一”,即为,(0nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn)+1 === x+1
,
即,-(x+1)
。
因此,对于正数,~x === -(x+1)
。
(2)再看负整数
对于32位负整数来说,它的二进制编码为,
1nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn
其中n
表示0
或者1
,
则,~x
为,
0uuuuuuu uuuuuuuu uuuuuuuu uuuuuuuu
其中u
为n
的取反结果。
设0uuuuuuu uuuuuuuu uuuuuuuu uuuuuuuu
的值为t
,
则1nnnnnnn nnnnnnnn nnnnnnnn nnnnnnnn
的值为,-(t+1)
,(逐位取反后,加一)。
因此,x === -(t+1)
,~x === t
,
即,~x === t === -(-(t+1) + 1) === -(x+1)
。
证毕。
参考
你不知道的JavaScript(中卷)
ECMAScript Language Specification
深入理解计算机系统
网友评论