【问题标题】:Iterating JavaScript object properties and arrays with for..in when ordering is important当排序很重要时,使用 for..in 迭代 JavaScript 对象属性和数组
【发布时间】:2011-03-08 13:03:23
【问题描述】:

这是一个古老的question,我知道在需要任何排序时不使用for..in 甚至对象的常见原因,但我最近从MDC 看到了这篇文章删除操作符。

跨浏览器问题

虽然 ECMAScript 使对象的迭代顺序依赖于实现,但似乎所有主流浏览器都支持基于最早添加的属性的迭代顺序(至少对于不在原型上的属性)。但是,在 Internet Explorer 的情况下,当对属性使用 delete 时,会导致一些令人困惑的行为,从而阻止其他浏览器使用像对象字面量这样的简单对象作为有序关联数组。在资源管理器中,虽然属性值确实设置为未定义,但如果稍后添加一个具有相同名称的属性,则该属性将在其旧位置迭代——而不是在迭代序列的末尾,正如人们可能期望的那样删除该属性,然后将其添加回来。

因此,如果您想在跨浏览器环境中模拟有序关联数组,则必须使用两个单独的数组(一个用于键,另一个用于值),或者构建一个单一属性的数组物体等。

似乎大多数主要浏览器 - Chrome、Safari、Firefox 和 Opera 都按插入顺序枚举对象属性。这是一个快速的test 来确认。它可能不适用于 IE,但不幸的是我无法访问 IE 来测试它。假设我们不从对象中删除任何内容,IE 在使用 for..in 迭代属性时是否也保留排序(6/7 起)?

此外,出于完全相同的原因,for..innever 推荐用于遍历数组,事实上Array.prototypeObject.prototype 更容易被篡改。但是,如果属性实际上是按插入顺序枚举的,那么我想for..in 也可以用于循环数组,具有以下问题-Array.prototype 上列出的属性也将被枚举。

有两种解决方案:

使用hasOwnProperty检查属性是否属于对象

for(var index in object) {
    if(object.hasOwnProperty(index)) {
        ..
    }
}

使用新的 ES5 语法来定义对象的属性并使它们不可枚举。这假设您可以完全控制基本原型的扩展,并且没有第三方代码直接在原型上添加任何内容。

Object.defineProperty(Array.prototype, "size", {
    enumerable: false,
    configurable: false,
    get: function() { return this.length; },
    set: undefined
});

如果满足上述所有条件,并且除非我遗漏了一些重要的东西,for..in 也可以安全地用于数组迭代:

var values = [1, 2, 3];

for(var i in values) {
    values[i];
}

所以我想这又回到了最初的问题。不管规范怎么说,IE 是否支持任何版本的插入顺序对象迭代?是否还有其他浏览器不支持此功能。

【问题讨论】:

    标签: javascript arrays object iteration


    【解决方案1】:

    除了这里提到的浏览器之外,还有更多的浏览器:还有其他当前的浏览器和未来的浏览器。在迭代对象的属性时,它们都没有义务实现任何排序,并且不会在很长一段时间内实现,因为最近的 ECMAScript 5 没有指定排序。事实上,Chrome development team is refusing to change its implementation 尽管有人要求它与其他浏览器保持一致。

    任何基于观察到的少数当前浏览器行为的假设充其量都是不可靠的;正如所观察到的,并非所有当前浏览器的行为都相同,未来的浏览器可能会选择不符合您的假设并且完全有权这样做。因此,我强烈建议不要依赖任何代码中的任何特定顺序。

    【讨论】:

      【解决方案2】:

      Tests 在 IE 5.5-6-7-8 上表现出完全相同的行为。属性是有序的。删除的属性保持其位置。

      但由于它是非标准行为,它可能会在 IE9 或任何浏览器的下一个版本中中断...

      也许我错过了什么,但重点是什么?与更强大的代码相比,保存一些字符,该代码与 3rd 方广告、跟踪系统、小部件等一起运行良好?

      更不用说支持移动浏览器,以及市场份额很小的桌面浏览器。

      【讨论】:

      • 感谢详尽的 IE 测试。只要我们知道不同浏览器的行为,我看不出这不能与 3rd 方代码一起使用。至于为什么它很重要,有许多使用有序关联数组的用例。您可以查看此问题的comment thread 了解更多详情。如果需要更强大的功能以及删除功能,我们当然可以编写自己的 OrderedMapLinkedMap 实现,但对于只读列表,它就可以工作:)
      【解决方案3】:

      在 WinXP pro SP2 上的 IE8 上运行测试可以确认 MDC 文章。 IE8 按照声明的顺序迭代成员;如果删除现有属性,然后重新分配,则保留其原始迭代位置。其他浏览器(我验证了 Chrome 5 和 Firefox 3)将重新分配的属性放在迭代顺序的末尾。

      【讨论】:

      • 谢谢@maerics - 这真的很有用。如果 IE 6/7 显示相同的行为,那就太好了。
      猜你喜欢
      • 2017-09-22
      • 2015-08-29
      • 2011-06-18
      • 1970-01-01
      • 1970-01-01
      • 2019-12-10
      • 2016-10-18
      • 2010-11-08
      • 2011-01-03
      相关资源
      最近更新 更多