什么是 JavaScript > > > 操作符? 如何使用它?

我在看 Mozilla 的代码,它向 Array 添加了一个 filter 方法,其中有一行代码让我感到困惑。

var len = this.length >>> 0;

我以前从来没有见过在 JavaScript 中使用 > > > 。 < br > 它是什么,它做什么?

39513 次浏览

这是 无符号右位移操作员。这与 有符号右位移运算符的区别在于,没签名右位移操作符(>>>)从左边填充零,而 签了右位移操作符(>>)填充符号位,从而在移位时保留数值的符号。

Driis 已经充分解释了操作符是什么以及它做什么。下面是它背后的含义/使用原因:

通过 0移动任何方向都会返回原始数字,并将 null转换为 0。似乎您正在查看的示例代码使用 this.length >>> 0来确保即使没有定义 this.lengthlen也是数字的。

对于许多人来说,按位操作并不清楚(而且道格拉斯·克罗克福特/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;

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

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

所以做一个没有实际效果的位操作,比如向右移动0比特的 >>0,是一个快速四舍五入的方法,可以确保它在32比特的 int 范围内。此外,三元 >>>运算符在执行无符号运算之后,会将计算结果转换为无符号整数,而不是其他运算符所做的有符号整数,因此它可以用来将负数转换为32位二进制补数版本的大数。使用 >>>0可以确保得到介于0和0xFFFFFFFF 之间的整数。

在这种情况下,这非常有用,因为 ECMAScript 以32位无符号整数的形式定义 Array 索引。因此,如果您试图以完全复制 ECMAScript Fifth Edition 标准的方式实现 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 的情况下实际使用 int,我不会感到惊讶。但这将是一个实现细节,你不会得到任何好处。)

>>>没签名右移操作符 (参见 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)

两个原因:

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

  2. 未定义 > > > 0 = 0(因为 JS 会尝试将 LFS 强制转换为数值上下文,所以对于“ foo”> > 0等等也可以这样做)

记住,JS 中的数字有一个双精度的内部表示。 这只是一个“快速”的基本输入长度方法。

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

在 Mozilla 的所有 数组额外的方法实现中都使用了无符号右移操作符,以确保 length属性是 无符号32位整数

数组对象的 length属性在规范中是 描述,如下所示:

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

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

Mozilla 的 数组附加值实现试图兼容 ECMAScript 5,看看 Array.prototype.indexOf方法的描述(& sect; 15.4.4.14) :

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

正如您所看到的,他们只是想复制 ToUint32方法的行为,以符合 ES3实现上的 ES5规范,正如我前面所说的,无符号右移算符是最简单的方法。

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