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


149

我正在查看Mozilla的代码,该代码向Array添加了一个过滤器方法,并且其中的一行代码使我感到困惑。

var len = this.length >>> 0;

我从未见过>>>在JavaScript中使用过。
这是什么,它做什么?


@CMS是的,此代码/问题来自于那些人。但是,这里的答复比以前的答复更加具体和有价值。
贾斯汀·约翰逊(

2
或者这是一个错误,或者Mozilla家伙假设this.length可能为-1。>>>是无符号移位运算符,因此var len始终为0或更大。
user347594'6

1
灰塞尔发现了一个使用它-翻转JS(道格·克罗克福德)的领主的实施Array.prototype.push/ Array.prototype.pop- hexmen.com/blog/2006/12/push-and-pop(虽然他做了测试,哈哈)。
丹·束

Answers:


211

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

虽然JavaScript的数字是双精度浮点(*),位运算符(<<>>&|~)在对32位整数运算的定义。执行按位运算会将数字转换为32位带符号的int,在进行计算然后再转换回Number之前,它会丢失所有小数和比32高的位。

因此,进行无实际影响的按位运算(例如,向右移位0位>>0)是舍入数字并确保其在32位int范围内的快速方法。此外,三元>>>运算符在执行无符号运算后,会将其计算结果转换为Number为无符号整数,而不是其他整数,因此可用于将负数转换为32位二进制补码。版本号很大。使用>>>0确保您拥有0到0xFFFFFFFF之间的整数。

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

(在现实中几乎没有实际的需要这是希望的人是不会被设置array.length0.5-11e21'LEMONS'。但是,这是我们正在谈论的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

(*:好吧,它们被定义为像float一样。出于性能原因,如果某个JavaScript引擎在可能的时候实际使用了int,这也不会令我感到惊讶。但这将是一个实现细节,您将无需花任何精力。的优势。)


2
深度说明和表格为+2,为-1,因为array.length会进行自我验证,并且不能将其任意设置为非整数或0的值(FF会引发此错误:)RangeError: invalid array length
贾斯汀·约翰逊(

4
但是,该规范故意允许在非Array上调用许多Array函数(例如,通过Array.prototype.filter.call),因此array实际上可能不是真实的Array:它可能是其他一些用户定义的类。(不幸的是,它不能可靠地成为NodeList,这是您真正想要执行的操作,因为它是一个宿主对象。实际上,您唯一可以做的就是使用arguments伪数组。)
bobince

很好的解释和很好的例子!不幸的是,这是Javascript的另一个疯狂方面。我只是不明白当您收到错误的类型时引发错误是多么可怕。可以允许动态键入,而不会发生每个偶然的错误来创建类型转换。:(
威廉姆森

“使用>>> 0可以确保您使用的是0到0xFFFFFFFF之间的整数。” if当试图确定评估的左侧不是整数时,该语句的外观是什么样的?'lemons'>>>0 === 0 && 0 >>>0 === 0评估为真?即使柠檬显然是一个词..?
Zze

58

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

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

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

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

Mozilla 数组附加实现尝试与ECMAScript 5兼容,请查看Array.prototype.indexOf方法说明(第15.4.4.14节):

1.令O为调用ToObject传递此值的结果 
   作为论点。
2.令lenValue为使用调用O的[[Get]]内部方法的结果 
   参数“长度”。
3.设len为ToUint32(lenValue)。
....

如您所见,他们只是想重现该ToUint32方法的行为以使其符合ES3实施中的ES5规范,而且正如我之前所说,无符号右移运算符是最简单的方法。


尽管链接数组的附加实现可能是正确的(或接近正确),但该代码仍然是一个不好的代码示例。也许甚至有一个澄清意图的评论也可以解决这种情况。
fmark 2010年

2
数组的长度是否可能不是整数?我无法想象,因此ToUint32对我而言似乎有点不必要。
马塞尔·科佩尔

7
@Marcel:请记住,大多数Array.prototype方法是有意通用的,可以在类似数组的对象上使用,例如Array.prototype.indexOf.call({0:'foo', 1:'bar', length: 2}, 'bar') == 1;。该arguments对象也是一个很好的例子。对于数组对象,无法更改length属性的类型,因为它们实现了特殊的[[Put ]]内部方法,并且在对length属性进行赋值后,再次转换ToUint32并采取了其他操作,例如删除上面的索引新的长度...
CMS 2010年

32

那是无符号右移运算符。此符号与带符号的右移位运算符之间的区别是,无符号的右移位运算符(>>>)从左侧开始填充零,而带符号的右移位运算符(>>)则填充符号位,因此移位时保留数值的符号。


伊万,那会把它偏移0位;那句话不会改变任何事情。
Dean J

3
@Ivan,通常,我想说的是,将值移位零位绝对没有任何意义。但这是Javascript,因此它背后可能有含义。我不是Java语言专家,但是它可能是确保值实际上是无类型Javasacript语言中的整数的一种方法。
driis

2
@Ivan,请参阅下面的贾斯汀的答案。实际上,这是确保len变量包含数字的一种方法。
driis

1
此外,>>>将其转换为整数,一元制+则不会。
递归

this.length >>> 0将有符号整数转换为无符号整数。就个人而言,当加载带有无符号整数的二进制文件时,我发现这很有用。
马特·帕金斯

29

德里斯已经充分解释了操作员是什么以及它做什么。这是其含义/使用原因:

将方向移动0确实会返回原始数字,并将转换null0。似乎您正在查看的示例代码正在使用this.length >>> 0以确保该代码len是数字的,即使this.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
虽然,您的第二个解决方案有时会评估为NaN..例如+{}...最好将两者结合起来:+length||0
James

1
this.length在数组对象的上下文中,除了非负整数(至少在FF中),不能为其他任何东西,因此在这里是不可能的。另外,{} || 1返回{},所以如果this.length是一个对象,您的情况就更好了。在第一种方法中也一元强制转换this.length的好处是它可以处理this.length为NaN的情况。编辑回应以反映这一点。
贾斯汀·约翰逊(

jslint也会抱怨var len = + this.length为“令人困惑的加法”。道格拉斯,你好挑剔!
Bayard Randel

道格拉斯很挑剔。尽管他的论点是明智的,通常是有根据的,但他所说的不是绝对的,也不是福音。
贾斯汀·约翰逊

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

因此,按照"cabbage"上面的示例,此处可能打算获取长度,或者,如果长度未定义或不是整数,则为0 。我认为在这种情况下可以肯定this.length不会< 0。尽管如此,我还是认为这个例子很讨厌,原因有二:

  1. 的行为<<<使用负数时,副作用可能不是在上面的例子意图(或可能发生)。

  2. 由于存在此问题,因此代码的意图并不明显

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

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

太... @ johncatfish是正确的吗?这是为了确保this.length为非负数?
安东尼2010年

4
是否可能-1 >>> 0会发生这种情况,如果真的发生了,是否真的希望将其转移到4294967295?似乎这样会使循环运行所需的次数。
deceze

@deceze:不this.length知道它的实现是不可能的。对于任何“合理”的实现,字符串的长度都不应为负,但是有人可能会争辩说,在“合理”的环境中,我们可以假设存在this.length总是返回整数的属性。
fmark 2010年

您说>>>不保留符号位..好的..所以,我不得不问,当我们处理负数时..在进行任何>>>或>>转换之前,它们是否为2s补余形式,或者它们是有符号整数形式,我们怎么知道?顺便说一句,我认为也许不是说2s补码有一个符号位..它是有符号表示法的替代方法,但是可以确定一个整数的符号
barlop 2016年

10

两个原因:

  1. >>>的结果是一个“整数”

  2. 未定义>>> 0 = 0(因为JS会尝试将LFS强制转换为数字上下文,所以这同样适用于“ foo” >>> 0,依此类推)

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

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


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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.