我正在查看来自 Mozilla 的代码,该代码向 Array 添加了过滤方法,其中有一行代码让我感到困惑。
var len = this.length >>> 0;
我以前从未见过 >>> 在 JavaScript 中使用。
它是什么,它有什么作用?
我正在查看来自 Mozilla 的代码,该代码向 Array 添加了过滤方法,其中有一行代码让我感到困惑。
var len = this.length >>> 0;
我以前从未见过 >>> 在 JavaScript 中使用。
它是什么,它有什么作用?
它不仅将非数字转换为数字,还将它们转换为可以表示为 32 位无符号整数的数字。
尽管 JavaScript 的数字是双精度浮点数 (*),但按位运算符 ( 、 、 和<<
)>>
是&
根据对 32 位整数的运算定义的。执行按位运算会将数字转换为 32 位有符号整数,在进行计算然后转换回数字之前丢失任何分数和高于 32 的位。|
~
因此,在没有实际效果的情况下进行按位运算,例如右移 0 位>>0
,是一种快速舍入数字并确保它在 32 位 int 范围内的方法。此外,三元>>>
运算符在进行无符号运算后,将其计算结果转换为数字作为无符号整数,而不是其他有符号整数,因此它可用于将负数转换为 32 位二进制补码版本为大数字。使用>>>0
确保你有一个介于 0 和 0xFFFFFFFF 之间的整数。
在这种情况下,这很有用,因为 ECMAScript 以 32 位无符号整数定义数组索引。因此,如果您尝试以array.filter
与 ECMAScript 第五版标准完全相同的方式实现,您可以像这样将该数字转换为 32 位无符号整数。
(实际上,这几乎没有实际需要,因为希望人们不会设置array.length
为0.5
、-1
或。但这是我们正在谈论的 JavaScript 作者,所以你永远不知道...... 1e21
)'LEMONS'
概括:
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 引擎出于性能原因在可能的情况下实际使用整数,我不会感到惊讶。但这将是一个实现细节,你不会采取任何的优势。)
Mozilla 的所有数组 extra 的方法实现中都使用了无符号右移运算符,以确保该length
属性是无符号 32 位整数。
length
数组对象的属性在规范中描述为:
每个 Array 对象都有一个长度属性,其值始终是小于 2 32的非负整数。
该运算符是实现它的最短方法,内部数组方法使用该ToUint32
操作,但该方法不可访问并且存在于规范中以用于实现目的。
Mozilla数组 extras实现尝试与ECMAScript 5兼容,请查看该Array.prototype.indexOf
方法的描述(第 15.4.4.14 节):
1. 令 O 为通过 this 值调用 ToObject 的结果 作为论据。 2. 设 lenValue 为调用 O 的 [[Get]] 内部方法的结果 论点“长度”。 3. 设 len 为ToUint32(lenValue)。 ……
如您所见,他们只是想重现该ToUint32
方法的行为以符合 ES3 实现的 ES5 规范,正如我之前所说,无符号右移运算符是最简单的方法。
Driis已经充分解释了运算符是什么以及它的作用。这是它背后的含义/为什么使用它:
将任何方向移动 do0
会返回原始数字并将null
转换为0
. 即使未定义 ,您正在查看的示例代码似乎也this.length >>> 0
用于确保它是数字的。len
this.length
对于许多人来说,按位运算不清楚(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;
>>>
是无符号右移运算符(参见 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
因此,这里可能要做的是获取长度,或者如果长度未定义或不是整数,则为 0,如上面的"cabbage"
示例所示。我认为在这种情况下可以安全地假设this.length
永远不会< 0
。尽管如此,我认为这个例子是一个讨厌的 hack,有两个原因:
<<<
使用负数时 的行为,在上面的示例中可能不打算(或可能发生)副作用。
代码的意图并不明显,正如这个问题的存在所证实的那样。
除非性能绝对关键,否则最佳实践可能是使用更具可读性的东西:
isNaN(parseInt(foo)) ? 0 : parseInt(foo)
两个原因:
>>> 的结果是“积分”
undefined >>> 0 = 0(因为 JS 会尝试将 LFS 强制转换为数字上下文,这也适用于 "foo" >>> 0 等)
请记住,JS 中的数字具有 double 的内部表示。这只是长度的基本输入理智的一种“快速”方式。
但是,-1 >>> 0(哎呀,可能不是所需的长度!)
下面的示例 Java 代码很好地解释了:
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