【问题标题】:What is the JavaScript >>> operator and how do you use it?什么是 JavaScript >>> 运算符以及如何使用它?
【发布时间】:2011-03-21 21:04:02
【问题描述】:

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

var len = this.length >>> 0;

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

【问题讨论】:

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

标签: javascript operators bit-shift


【解决方案1】:

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

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

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

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

(实际上,对此几乎没有实际需求,因为希望人们不会将 array.length 设置为 0.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

(*: 嗯,它们被定义为像浮点数一样。如果某些 JavaScript 引擎出于性能原因实际使用 int,我不会感到惊讶。但这将是一个你不会得到的实现细节充分利用。)

【讨论】:

  • +2 深度描述和表格,-1 因为 array.length 自我验证并且不能任意设置为非整数或 0 的任何值(FF 抛出此错误:RangeError: invalid array length) .
  • 然而,规范故意允许在非数组上调用许多数组函数(例如通过Array.prototype.filter.call),所以array实际上可能不是真正的Array:它可能是其他一些用户定义的类。 (不幸的是,它不能可靠地成为 NodeList,这是您真正想要这样做的时候,因为那是一个主机对象。这使得您真正可以做到这一点的唯一地方是 arguments 伪数组。 )
  • 很好的解释和很好的例子!不幸的是,这是 Javascript 的另一个疯狂方面。我只是不明白当你收到错误的类型时抛出错误有什么可怕的。可以允许动态类型,而不会允许每个意外错误创建类型转换。 :(
  • " 使用 >>>0 可确保您得到一个介于 0 和 0xFFFFFFFF 之间的整数。" 在尝试识别时,if 语句会是什么样子左边的评价不是一个int? 'lemons'>>>0 === 0 && 0 >>>0 === 0 评估为真?即使柠檬显然是一个词..?
【解决方案2】:

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

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

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

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

Mozilla array 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 规范,正如我之前所说,unsigned right shift operator 是最简单的方法。

【讨论】:

  • 虽然链接的 array extras 实现可能是正确的(或接近正确的),但代码仍然是一个糟糕的代码示例。也许即使是澄清意图的评论也可以解决这种情况。
  • 数组的长度有可能不是一个整数吗?我无法想象,所以这种ToUint32对我来说似乎有点不必要。
  • @Marcel:请记住,大多数Array.prototype 方法是故意通用的,它们可以用于类数组对象,例如Array.prototype.indexOf.call({0:'foo', 1:'bar', length: 2}, 'bar') == 1;arguments 对象也是一个很好的例子。对于数组对象,不可能改变length属性的类型,因为它们实现了一个特殊的[[Put]]内部方法,并且当对length属性进行赋值时, 再次转换 ToUint32 并采取其他操作,例如删除新长度以上的索引...
【解决方案3】:

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

【讨论】:

  • Ivan,这会将其移动 0 个位置;该声明不会改变任何事情。
  • @Ivan,通常情况下,我会说将值移动零位绝对没有意义。但这是 Javascript,所以它背后可能有一个含义。我不是 Javascript 专家,但它可能是一种确保值实际上是无类型 Javasacript 语言中的整数的方法。
  • @Ivan,请参阅下面贾斯汀的回答。这实际上是一种确保 len 变量包含数字的方法。
  • 此外,>>> 转换为整数,而一元 + 不这样做。
  • this.length >>> 0 将有符号整数转换为无符号整数。就我个人而言,我发现这在加载包含无符号整数的二进制文件时很有用。
【解决方案4】:

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

将任何方向移动0 确实会返回原始数字并将null 转换为0。您正在查看的示例代码似乎使用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; 

【讨论】:

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

>>>无符号 右移运算符 (see p. 76 of the JavaScript 1.5 specification),与>> 相对,有符号 右移运算符移位运算符。

>>> 改变了负数移位的结果,因为它在移位时不保留符号位。这样做的后果可以通过解释器的例子来理解:

$ 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)

【讨论】:

  • Sooo...@johncatfish 是正确的吗?是为了保证this.length是非负数?
  • -1 >>> 0 的情况是否会发生,如果发生,是否真的需要将其转移到 4294967295?似乎这会导致循环运行次数超出必要的次数。
  • @deceze:没有看到this.length的实现是不可能知道的。对于任何“健全”的实现,字符串的长度永远不应该是负数,但是有人可能会争辩说,在“健全”的环境中,我们可以假设存在一个始终返回整数的 this.length 属性。
  • 你说 >>> 不保留符号位.. 好的.. 所以,我不得不问,当我们处理负数时.. 在任何 >>> 或 > > 转换,它们是 2s 恭维形式,还是带符号整数形式,我们怎么知道?顺便说一句,我认为可能没有说 2s 补码有符号位.. 它是有符号表示法的替代品,但可以确定整数的符号
【解决方案6】:

两个原因:

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

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

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

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

【讨论】:

    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-10
      • 2015-03-13
      • 2013-10-25
      • 2014-11-19
      相关资源
      最近更新 更多