我在查看 Mozilla 的代码,它向数组添加了一个 filter 方法,其中有一行代码让我感到困惑。
var len = this.length >>> 0;
我之前从未见过JavaScript中使用 >>> 。
它是什么?它有什么作用?
我在查看 Mozilla 的代码,它向数组添加了一个 filter 方法,其中有一行代码让我感到困惑。
var len = this.length >>> 0;
我之前从未见过JavaScript中使用 >>> 。
它是什么?它有什么作用?
1>>>0 === 1
-1>>>0 === 0xFFFFFFFF -1>>0 === -1
1.7>>>0 === 1
0x100000002>>>0 === 2
1e21>>>0 === 0xDEA00000 1e21>>0 === -0x21600000
Infinity>>>0 === 0
NaN>>>0 === 0
null>>>0 === 0
'1'>>>0 === 1
'x'>>>0 === 0
Object>>>0 === 0
(*:其实,它们被定义为像浮点数一样运作。如果出于性能原因某些JavaScript引擎确实使用整数,那也不会让我感到惊讶。但这是一些你无法从中获得任何优势的具体实现细节。)
RangeError: invalid array length
)。 - Justin JohnsonArray.prototype.filter.call
),因此 array
可能实际上并不是一个真正的 Array
:它可能是某些其他用户定义的类。(不幸的是,它不能可靠地成为 NodeList,当你真正想要这样做时,因为那是宿主对象。这只留下了唯一一个你真正会这样做的地方 - arguments
伪数组。) - bobincelength
属性是一个无符号32位整数。length
属性在规范中被描述为:ToUint32
操作,但该方法不可访问,并且存在于规范中以供实现目的。Array.prototype.indexOf
方法的描述(§15.4.4.14):ToUint32
方法的行为以符合 ES5 规范在 ES3 实现上的要求,而且就像我之前说的那样,无符号右移运算符是最简单的方法。Array.prototype
方法是有意地通用的,它们可以用于类似数组的对象,例如Array.prototype.indexOf.call({0:'foo', 1:'bar', length: 2}, 'bar') == 1;
。 arguments
对象也是一个很好的例子。对于纯数组对象,无法更改length
属性的类型,因为它们实现了一个特殊的[[Put
]]内部方法,当对length
属性进行赋值时,会再次将其转换为ToUint32
并采取其他操作,例如删除新长度以上的索引... - Christian C. Salvadó>>>
转换为整数,而一元运算符 +
不会这样做。 - recursiveDriis 已经解释了位运算符是什么以及它的作用。下面是它的含义/为什么使用它:
将任何方向移动 0
次会返回原始数字,并将 null
强制转换为 0
。看起来你正在查看的示例代码使用 this.length >>> 0
来确保即使未定义 this.length
,也可以确保 len
是数字。
对于许多人来说,位运算不太清晰(Douglas Crockford/jslint 建议不要使用这些东西)。这并不意味着使用它是错误的,但更有利和熟悉的方法存在,可以使代码更易读。更清晰的确保 len
是 0
的方法是以下两种之一。
// Cast this.length to a number
var len = +this.length;
或者// Cast this.length to a number, or use 0 if this.length is
// NaN/undefined (evaluates to false)
var len = +this.length || 0;
NaN
。例如 +{}
... 最好将两者结合起来:+length||0
。 - James>>>
是无符号右移位运算符(参见JavaScript 1.5规范的第76页),与>>
有所不同,后者是带符号的右移位运算符。
>>>
更改了负数移位的结果,因为它在移位时不保留符号位。这样做的后果可以通过解释器的示例来理解:
$ 1 >> 0
1
$ 0 >> 0
0
$ -1 >> 0
-1
$ 1 >>> 0
1
$ 0 >>> 0
0
$ -1 >>> 0
4294967295
$(-1 >>> 0).toString(16)
"ffffffff"
$ "cabbage" >>> 0
0
this.length
永远不会< 0
。 尽管如此,我认为这个示例是一个恶劣的hack,有两个原因:
使用负数时<<<
的行为是副作用,可能不是上面示例中预期的(或可能发生的)。
代码的意图不明显,正如这个问题的存在证明的那样。
最好的做法可能是使用更易读的内容,除非性能绝对关键:
isNaN(parseInt(foo)) ? 0 : parseInt(foo)
-1 >>> 0
是否有可能出现?如果是,将其移位为4294967295真的是可取的吗?似乎这会导致循环运行比必要次数还要多一些。 - decezethis.length
的实现,是不可能知道的。对于任何“理智”的实现,字符串的长度永远不应该是负数,但是我们可以认为在一个“理智”的环境中,存在一个this.length
属性,它总是返回一个整数。 - fmark两个原因:
>>> 的结果是一个“整数”
undefined >>> 0 = 0(由于JS会尝试将LFS强制转换为数字上下文,因此这也适用于“foo”>>> 0等)
请记住,JS中的数字具有双重内部表示方式。这只是一种基本输入合理性的“快速”方法。
然而,-1 >>> 0(糟糕,可能不是所需的长度!)
int x = 64;
System.out.println("x >>> 3 = " + (x >>> 3));
System.out.println("x >> 3 = " + (x >> 3));
System.out.println(Integer.toBinaryString(x >>> 3));
System.out.println(Integer.toBinaryString(x >> 3));
输出如下:
x >>> 3 = 536870904
x >> 3 = -8
11111111111111111111111111000
11111111111111111111111111111000
Array.prototype.push
/Array.prototype.pop
的实现 - http://hexmen.com/blog/2006/12/push-and-pop/(尽管他进行了测试,哈哈)。 - Dan Beam