什么是 JavaScript >>>运算符,如何使用它?

What is the JavaScript >>> operator and how do you use it?

提问人:Kenneth J 提问时间:12/1/2009 最后编辑:Jon SeigelKenneth J 更新时间:2/9/2023 访问量:41309

问:

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

var len = this.length >>> 0;

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

JavaScript 运算符 位移

评论

0赞 Justin Johnson 12/1/2009
@CMS 没错,这个代码/问题来自那些;但是,这里的答复比以前的答复更具体、更有价值。
2赞 user347594 6/21/2010
或者这是一个错误,或者Mozilla的人认为this.length可能是-1。>>> 是无符号的移位运算符,因此 var len 将始终为 0 或更大。
2赞 Dan Beam 6/21/2010
Ash Searle 发现了它的用途 - 将 JS 之王(Doug Crockford)的实现推翻为 / - hexmen.com/blog/2006/12/push-and-pop(尽管他做了测试,哈哈)。Array.prototype.pushArray.prototype.pop

答:

34赞 driis 12/1/2009 #1

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

评论

0赞 Dean J 12/1/2009
伊万,这将使它移动 0 位;这种说法不会改变任何事情。
3赞 driis 12/1/2009
@Ivan,通常情况下,我会说将值移动零位是完全没有意义的。但这是 Javascript,所以它背后可能有一个含义。我不是 Javascript 专家,但它可能是一种确保该值实际上是无类型 Javasacript 语言中的整数的方法。
2赞 driis 12/1/2009
@Ivan,请参阅下面的 Justin 的回答。这实际上是一种确保 len 变量包含数字的方法。
1赞 recursive 6/29/2012
此外,转换为整数,一元数不这样做。>>>+
0赞 Matt Parkins 7/11/2013
this.length >>> 0 将有符号整数转换为无符号整数。就我个人而言,我发现这在加载带有无符号整数的二进制文件时很有用。
29赞 Justin Johnson 12/1/2009 #2

Driis 已经充分解释了操作员是什么以及它的作用。以下是它背后的含义/为什么使用它:

将任何方向移动为 do 将返回原始数字,并将强制转换为 。您正在查看的示例代码似乎用于确保即使未定义也是数字。0null0this.length >>> 0lenthis.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; 

评论

1赞 James 12/1/2009
虽然,您的第二个解决方案有时会评估为 ..例如 ...最好将两者结合起来:NaN+{}+length||0
1赞 Justin Johnson 12/1/2009
this.length 在数组对象的上下文中,除了非负整数之外,它不能是任何东西(至少在 FF 中),所以这里不可能。另外,{} ||1 返回 {},所以如果 this.length 是一个对象,你也好不到哪里去。在第一种方法中,一元转换 this.length 的好处是它可以处理 this.length 为 NaN 的情况。编辑了回复以反映这一点。
0赞 Bayard Randel 7/28/2010
jslint 也会抱怨 var len = +this.length 是“令人困惑的加号”。道格拉斯,你太挑剔了!
0赞 Justin Johnson 7/28/2010
道格拉斯很挑剔。虽然他的论点是明智的,而且通常是有根据的,但他所说的不是绝对的,也不是福音的。
239赞 bobince 12/1/2009 #3

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

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

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

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

实际上,这几乎没有实际必要,因为希望人们不会将 、 或 设置为 。array.length0.5-11e21'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 引擎实际上在可能的情况下使用 ints,我不会感到惊讶。但这将是一个你不会利用的实现细节。

评论

2赞 Justin Johnson 12/1/2009
+2 在深度描述和表中,-1 因为 array.length 会验证自身,并且不能任意设置为非整数或 0 的任何内容(FF 抛出此错误:)。RangeError: invalid array length
4赞 bobince 12/1/2009
然而,该规范故意允许在非 Array 上调用许多 Array 函数(例如通过 ),因此实际上可能不是真正的:它可能是其他一些用户定义的类。 (不幸的是,它不能可靠地成为 NodeList,这是您真正想要这样做的时候,因为它是一个主机对象。这留下了你唯一可以实际这样做的地方,即伪数组。Array.prototype.filter.callarrayArrayarguments
0赞 Mike Williamson 9/27/2017
很好的解释和很好的例子!不幸的是,这是 Javascript 的另一个疯狂方面。我只是不明白当你收到错误的类型时抛出错误有什么可怕的。可以允许动态类型化,而不允许每个意外错误创建类型转换。:(
0赞 Zze 3/28/2018
“使用 >>>0 可确保整数介于 0 和 0xFFFFFFFF 之间。”当试图确定评估的左侧不是 int 时,语句会是什么样子? 评估为真?即使柠檬显然是一个词..?if'lemons'>>>0 === 0 && 0 >>>0 === 0
15赞 fmark 6/21/2010 #4

>>>无符号的右移运算符参见 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

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

  2. 代码的意图并不明显,因为这个问题的存在验证了这一点。

最佳做法可能是使用更具可读性的内容,除非性能绝对关键:

isNaN(parseInt(foo)) ? 0 : parseInt(foo)

评论

0赞 Anthony 6/21/2010
那么......@johncatfish是正确的?这是为了确保this.length是非负数的?
4赞 deceze 6/21/2010
这种情况是否会发生,如果是这样,是否真的需要将其转移到4294967295?似乎这会导致循环运行的次数比必要的次数多。-1 >>> 0
0赞 fmark 6/21/2010
@deceze:没有看到实现是不可能知道的。对于任何“理智”的实现,字符串的长度永远不应该是负数,但有人可能会争辩说,在“理智”的环境中,我们可以假设存在一个总是返回整数的属性。this.lengththis.length
0赞 barlop 2/18/2016
你说>>>不保留符号位..还行。。所以,我不得不问,当我们处理负数时......在进行任何>>>或>>转换之前,它们是 2s 符合形式,还是有符号整数形式,我们怎么知道?顺便说一句,我认为 2s 补码可能没有说有符号位。.它是有符号表示法的替代方法,但可以确定整数的符号
10赞 user166390 6/21/2010 #5

原因有二:

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

  2. undefined >>> 0 = 0 (由于 JS 会尝试将 LFS 强制到数字上下文,这也适用于“foo”>>> 0 等)

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

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

59赞 Christian C. Salvadó 6/21/2010 #6

Mozilla 的所有 array extra 方法实现中使用了无符号右移运算符,以确保该属性是无符号的 32 位整数length

数组对象的属性在规范中描述为length

每个 Array 对象都有一个 length 属性,其值始终是小于 232 的非负整数。

此运算符是实现它的最短方法,内部数组方法使用 ToUint32 操作,但该方法不可访问,并且存在于规范中以实现目的。

Mozilla array extras 实现尝试与 ECMAScript 5 兼容,请查看该方法的描述 (§ 15.4.4.14):Array.prototype.indexOf

1. Let O be the result of calling ToObject passing the this value 
   as the argument.
2. Let lenValue be the result of calling the [[Get]] internal method of O with 
   the argument "length".
3. Let len be ToUint32(lenValue).
....

正如你所看到的,他们只是想在 ES3 实现上重现该方法的行为以符合 ES5 规范,正如我之前所说,无符号右移运算符是最简单的方法。ToUint32

评论

0赞 fmark 6/21/2010
虽然链接数组附加实现可能是正确的(或接近正确),但代码仍然是一个糟糕的代码示例。也许即使是澄清意图的评论也可以解决这种情况。
2赞 Marcel Korpel 6/21/2010
数组的长度可能不是整数吗?我无法想象,所以这对我来说似乎有点没有必要。ToUint32
7赞 Christian C. Salvadó 6/22/2010
@Marcel:请记住,大多数方法都是有意泛型的,它们可以用于类似数组的对象,例如。该对象也是一个很好的例子。对于数组对象,不可能更改属性的类型,因为它们实现了一个特殊的 [[Put]] 内部方法,并且当对属性进行赋值时,会再次转换并执行其他操作,例如删除高于新长度的索引......Array.prototypeArray.prototype.indexOf.call({0:'foo', 1:'bar', length: 2}, 'bar') == 1;argumentslengthlengthToUint32
0赞 nitinsridar 8/2/2019 #7

下面的示例 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