Javascript中的排序属性已损坏


15

我需要遍历一个JavaScript对象,将其视为具有自定义键的数组。我知道这不受完全支持,因为属性没有内在的顺序,但是由于我总是对属性进行重新排序,因此我发现这种方法简单可靠。

当键是数字或可以转换为数字的字符串时,会发生此问题。

当我运行此代码时:

var test1 = {4294966222:"A",4294966333:"A",4294966111:"A"};
var test2 = {4294968222:"A",4294968333:"A",4294968111:"A"};
        
for (var k in test1) {console.log(k);}
console.log("---");
for (var k in test2) {console.log(k);}

输出为:

4294966111
4294966222
4294966333
---
4294968222
4294968333
4294968111

意思是:

  • (test1)如果键低于2 ^ 32(4,294,967,296),则会自动对它们进行重新排序,从小到大
  • (test2)如果键大于2 ^ 32,则不会重新排序。

问题是:为什么会这样?

由于我测试过的所有浏览器(Google Chrome 79.0,Mozilla Firefox 71.0,Microsoft Edge 44.18362,Internet Explorer 11.535)都同意此输出,因此必须有一些正式规范。

更新资料

在发现这是一个门槛问题之前,我测试了很多数字。我发现奇怪的是,序列2、3、1的行为与以相同方式排序的三个时间戳不同。


我的猜测是哈希码的计算方式,但这并不是您所提问题的真正答案。
马里奥·韦纳里

1
我不认为它在真正的口语意义上是被破坏的,它们不能保证值可以按顺序进行迭代,因为它可以任意运行,因为您可以检查developer.mozilla.org/en-US/docs/Web / JavaScript / Reference /… “注意:for ... in不应用于在索引顺序很重要的数组上进行迭代。” 它们仅保证对集合中的每个元素进行迭代。像forEach这样的东西确实确实通过将项目以升序顺序遍历来考虑到该顺序 ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
Mr.Toxy

作为记录,您可以通过登录test1和直接查看问题test2。我认为“问题”来自规范的V8实现中的关键缓存。
塞布勒

在2 ^ 32以下,您的属性名称与内部属性引用相似地是巧合地排列的。您可以并且不应依赖于对象属性的顺序,因为根据定义,它们没有顺序,并且可以包含对象固有的属性。始终将对象投射/映射到数组中,对数组进行排序,然后在顺序很重要时遍历该对象。
user3154108 '19

1
@ Mr.Toxy那是因为这些属性42949683334294968111大于2 ** 32(是4294967296)。因此,它们不是数组索引,因此按属性创建顺序(而不是按升序顺序)进行迭代-这恰如他们所期望的那样。(请参阅我的回答)
SomePerformance

Answers:


4

这是预期的。根据规范,迭代属性的方法OrdinaryOwnPropertyKeys执行以下操作:

  1. 对于作为数组索引的O的每个自己的属性键P ,按数字索引升序执行

    一个。将P添加为键的最后一个元素。

  2. 对于O的每个自己的属性键P(它是String而不是数组索引),按照属性创建的时间顺序升序进行

    一个。将P添加为键的最后一个元素。

升序适用于属于数组索引的属性。

那么,什么是“数组索引”?查找

整数索引是一个字符串值的属性键,它是规范的数字字符串(请参见7.1.21),其数字值为+0或正整数≤2 ^ 53-1。数组索引是其数字为整数的整数索引值i在+0≤i <2 ^ 32-1。

因此,大于2 ^ 32的数字属性不是数组索引,因此按属性创建的顺序进行迭代。然而,数值属性是小于2^32 阵列indicies,并在上升的数字顺序遍历。

因此,例如:

1:数组索引,将在数字上迭代

10:数组索引,将在数字上迭代

4294968111:大于2 ** 32,将数组索引完成后按照属性创建顺序进行迭代

9999999999999:大于2 ** 32,将数组索引完成后按照属性创建顺序进行迭代

另外,请记住,与流行的看法相反,由于第4阶段的for-in迭代建议,规范保证了属性迭代的顺序。


2

这与遍历对象的键的方式有关。

根据ES6规范,应为:

9.1.12 [[OwnPropertyKeys]] ( )

When the [[OwnPropertyKeys]] internal method of O is called the following steps are taken:

    Let keys be a new empty List.
    For each own property key P of O that is an integer index, in ascending numeric index order
        Add P as the last element of keys.
    For each own property key P of O that is a String but is not an integer index, in property creation order
        Add P as the last element of keys.
    For each own property key P of O that is a Symbol, in property creation order
        Add P as the last element of keys.
    Return keys.

http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys

这意味着,如果将键的值转换为无符号的53位数字,然后将其保留不变,则将其视为整数索引,该索引按升序排列。

如果失败,则将其视为字符串键,并按照将其添加到对象的方式进行排序。

这里要注意的是,所有主流浏览器都尚未遵循此规范,而是使用数组索引,该索引限制为正数,最大为2 ^ 32-1。因此,超出该限制的任何内容实际上都是字符串键。

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.