164

我正在查看来自 Mozilla 的代码,该代码向 Array 添加了过滤方法,其中有一行代码让我感到困惑。

var len = this.length >>> 0;

我以前从未见过 >>> 在 JavaScript 中使用。
它是什么,它有什么作用?

4

7 回答 7

231

它不仅将非数字转换为数字,还将它们转换为可以表示为 32 位无符号整数的数字。

尽管 JavaScript 的数字是双精度浮点数 (*),但按位运算符 ( 、 、 和<<)>>&根据对 32 位整数的运算定义的。执行按位运算会将数字转换为 32 位有符号整数,在进行计算然后转换回数字之前丢失任何分数和高于 32 的位。|~

因此,在没有实际效果的情况下进行按位运算,例如右移 0 位>>0,是一种快速舍入数字并确保它在 32 位 int 范围内的方法。此外,三元>>>运算符在进行无符号运算后,将其计算结果转换为数字作为无符号整数,而不是其他有符号整数,因此它可用于将负数转换为 32 位二进制补码版本为大数字。使用>>>0确保你有一个介于 0 和 0xFFFFFFFF 之间的整数。

在这种情况下,这很有用,因为 ECMAScript 以 32 位无符号整数定义数组索引。因此,如果您尝试以array.filter与 ECMAScript 第五版标准完全相同的方式实现,您可以像这样将该数字转换为 32 位无符号整数。

(实际上,这几乎没有实际需要,因为希望人们不会设置array.length0.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 引擎出于性能原因在可能的情况下实际使用整数,我不会感到惊讶。但这将是一个实现细节,你不会采取任何的优势。)

于 2009-11-30T22:13:32.547 回答
59

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 规范,正如我之前所说,无符号右移运算符是最简单的方法。

于 2010-06-21T04:12:51.153 回答
34

那是无符号右位移运算符。这与有符号右位移运算符之间的区别在于,无符号右位移运算符 ( >>> ) 从左边用零填充,而有符号右位移运算符 ( >> ) 用符号位填充,因此移位时保留数值的符号。

于 2009-11-30T21:01:31.747 回答
29

Driis已经充分解释了运算符是什么以及它的作用。这是它背后的含义/为什么使用它:

将任何方向移动 do0会返回原始数字并将null转换为0. 即使未定义 ,您正在查看的示例代码似乎也this.length >>> 0用于确保它是数字的。lenthis.length

对于许多人来说,按位运算不清楚(Douglas Crockford/jslint 建议不要使用这种东西)。这并不意味着这样做是错误的,而是存在更有利和熟悉的方法来使代码更具可读性。一种更明确的方法来确保它len0以下两种方法之一。

// 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; 
于 2009-11-30T21:10:37.073 回答
15

>>>无符号右移运算符参见 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,有两个原因:

  1. <<<使用负数时 的行为,在上面的示例中可能不打算(或可能发生)副作用。

  2. 代码的意图并不明显,正如这个问题的存在所证实的那样。

除非性能绝对关键,否则最佳实践可能是使用更具可读性的东西:

isNaN(parseInt(foo)) ? 0 : parseInt(foo)
于 2010-06-21T03:49:04.277 回答
10

两个原因:

  1. >>> 的结果是“积分”

  2. undefined >>> 0 = 0(因为 JS 会尝试将 LFS 强制转换为数字上下文,这也适用于 "foo" >>> 0 等)

请记住,JS 中的数字具有 double 的内部表示。这只是长度的基本输入理智的一种“快速”方式。

但是,-1 >>> 0(哎呀,可能不是所需的长度!)

于 2010-06-21T03:50:29.133 回答
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
于 2019-08-02T13:50:47.990 回答