将任意值转换为自然数
前几天和小马在MDC上看见Array.reduce的实现中有一行很有意思的代码:
if (!Array.prototype.reduce)
{
Array.prototype.reduce = function(fun /*, initial*/)
{
var len = this.length >>> 0;
if (typeof fun != "function")
throw new TypeError();
...
>>>是零填充右移位操作符,犀牛书上有很简单的解释:
The >>> operator is just like the >> operator, except that the bits shifted in on the left are always zero, regardless of the sign of the first operand. For example, -1 >> 4 evaluates to -1, but -1 >>> 4 evaluates to 268435455 (0x0fffffff).
上面的解释无法让人明白this.length >>> 0的作用,位移零位,不是没有移动吗?
查阅下ECMA-262规范,11.7.3 The Unsigned Right Shift Operator 一节:
The production ShiftExpression : ShiftExpression >>> AdditiveExpression is evaluated as follows:
1. Evaluate ShiftExpression.
2. Call GetValue(Result(1)).
3. Evaluate AdditiveExpression.
4. Call GetValue(Result(3)).
5. Call ToUint32(Result(2)).
6. Call ToUint32(Result(4)).
7. Mask out all but the least significant 5 bits of Result(6), that is, compute Result(6) & 0x1F.
8. Perform zero-filling right shift of Result(5) by Result(7) bits. Vacated bits are filled with zero. The
result is an unsigned 32 bit integer.
9. Return Result(8)
可以看出,在第5步和第6步中,有ToUnit32操作,使得最后结果必然为整数。
再来看看Douglas在《JavaScript: The Good Parts》一书中对位操作符的判定:
In Java, the bitwise operators work with integers. JavaScript doesn’t have integers. It only has double precision floating-point numbers. So, the bitwise operators convert their number operands into integers, do their business, and then convert them back. In most languages, these operators are very close to the hardware and very fast. In JavaScript, they are very far from the hardware and very slow. JavaScript is rarely used for doing bit manipulation.
Douglas不推荐使用位操作符,因为性能很低。不过我们可以看出的是,位操作符只操作number类型,也就是说,如果位操作符的操作数不是number类型的,会在内部被转换成number类型中的整数值:
alert(1.2 >>> 0); alert(NaN >>> 0); alert(null >>> 0); alert(window >>> 0); alert(-2 >>> 0); alert(Infinity >>> 0);
简单总结如下:
- 对于大于等于0的number类型值val,等价为
Math.floor(val). - 对于小于0的number类型值,会按照 >>> 操作符的定义转换为正数。
- 对于非number类型的任何值,统统转换为0.
其中第3条是我们最感兴趣的,能很好的解释this.length >>> 0的用意。在我们不能预测某个变量的值,但又想将其当作整数来用时,利用这个技巧可以让我们的代码非常鲁棒。
延伸思考:
转换成数值还可以用Number(someVar), 但是恼人的是Not a Number is a number, 不是我们想要的。
还有一个常用的类似技巧是String(someVar), 可以将任意值转换为字符串。这在某些时候也是非常有用的,比如:
var a= parseJSON(json)['someName']; var b = String(a).replace(/^s+|s+$/g, '');
如果直接用a.replace, 当a是一串数字时,就会出错了。可以看下这篇文章:谨慎使用YAHOO.lang.substitute.
最后留几个问题:
- 为什么要用
>>> 0, 而不用>> 0? 后者也可以将任意非number类型值转换为零。 - Array.reduce的实现代码里,
this.length >>> 0是否有杞人忧天之嫌?虽然程序要尽量鲁棒,但如此写代码,是否太过分了? - Douglas说性能低下所以不用,性能究竟有多低呢?
我的思考:(请想想后,再点击查看)

March 17th, 2009 on 1:10
var len = this.length || 0; 也有异曲同工之妙。
http://glog.xianyun.org/2008/03/blog-post_31.html
March 17th, 2009 on 7:48
@闲耕:window || 0 就不行了
March 17th, 2009 on 10:57
这是第一个评论吧,当时打错了,以第二个评论为准(大概被删了),应该是“位或”,不是“或”:
var len = this.length | 0;
March 17th, 2009 on 15:08
用“位或” | 转整数应该也可以,还节省2个字符
Evaluate A.
2.Call GetValue(Result(1)).
3.Evaluate B.
4.Call GetValue(Result(3)).
5.Call ToInt32(Result(2)).
6.Call ToInt32(Result(4)).
7.Apply the bitwise operator @ to Result(5) and Result(6). The result is a signed 32 bit integer.
8.Return Result(7).
March 17th, 2009 on 17:39
是否可以用来 判断 是否为 负数?
March 17th, 2009 on 19:58
位或 | 的确也可以,不错
March 28th, 2009 on 17:57
alert(1222140417000|0)
会变成负数
April 1st, 2009 on 19:16
alert(parseInt(1.2) || 0);
alert(parseInt(NaN) || 0);
alert(parseInt(null) || 0);
alert(parseInt(window) || 0);
alert(parseInt(-2) || 0);
alert(parseInt(Infinity) || 0);
May 6th, 2010 on 12:26
-2 | 0
与
-2 >>>0
有区别
leave a reply