【问题标题】:Sorting properties in Javascript is brokenJavascript中的排序属性被破坏
【发布时间】:2019-12-19 10:33:05
【问题描述】:

我需要遍历一个 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 与以相同方式排序的三个时间戳的行为不同。

【问题讨论】:

  • 我的猜测是哈希码是如何计算的,但这不是您问题的真正答案。
  • 我不认为它在真正的口语意义上被破坏了,他们不保证这些值会按顺序迭代,因为它是任意运行的,你可以检查developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…"注意: for...in 不应该用于遍历索引顺序很重要的数组。”它们只保证对集合中的每个元素进行迭代。像 forEach 这样的东西确实通过按升序遍历项目来考虑顺序ecma-international.org/ecma-262/5.1/#sec-15.4.4.18
  • 记录一下,直接登录test1test2就可以看到问题了。我认为“问题”来自规范的 V8 实现中的密钥缓存。
  • 更多的是,在 2^32 以下,您的属性名称是巧合地排序,类似于内部属性引用。您可以而且不应该依赖对象属性的顺序,因为根据定义它们没有顺序并且可以包含对象固有的属性。始终将您的对象转换/映射到数组中,对数组进行排序,然后在顺序很重要时循环遍历它。
  • @Mr.Toxy 那是因为42949683334294968111 的属性大于2 ** 32(即4294967296)。因此,它们不是数组索引,因此它们按属性创建顺序进行迭代,而不是按数字升序进行迭代——这正是它们在小提琴中所做的,正如预期的那样。 (见我的回答)

标签: javascript


【解决方案1】:

这是意料之中的。根据specification,迭代属性的方法OrdinaryOwnPropertyKeys

  1. 对于O的每个自己的属性键P是一个数组索引,按数字索引升序,做

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

  2. 对于O的每个自己的属性键P,它是一个String但不是一个数组索引,按照属性创建的时间升序,做

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

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

那么,什么是“数组索引”? Look it up::

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

因此,大于 2^32 的数值属性不是数组索引,因此按属性创建的顺序进行迭代。但是,小于2^32 的数值属性数组索引,并按数值升序迭代。

所以,例如:

1: 数组索引,将被数值迭代

10: 数组索引,将被数字迭代

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

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

另外,请记住,与普遍的看法相反,属性迭代顺序 也得到规范的保证,这要归功于第 4 阶段的for-in iteration proposal

【讨论】:

    【解决方案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 位数字后保持不变 并返回它被视为一个整数索引,它按数字升序排序。

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

    这里的问题是所有主流浏览器还没有遵循这个规范 并改用 数组索引,它被限制为最多为 的正数。 所以任何超过这个限制的东西实际上都是一个字符串键。

    【讨论】:

      猜你喜欢
      • 2016-03-06
      • 2015-08-24
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 1970-01-01
      • 2021-12-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多