在订购时使用for..in迭代JavaScript对象属性和数组非常重要

时间:2010-06-26 03:00:36

标签: javascript arrays object iteration

这已经是一个古老的question了,我知道在需要任何排序时不使用for..in或甚至是对象的通常原因,但我最近发现了这篇文章来自删除操作符上的MDC

跨浏览器问题

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

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

似乎大多数主要浏览器 - Chrome,Safari,Firefox和Opera都按插入顺序枚举对象属性。这是一个快速test来确认这一点。它可能无法在IE上运行,但遗憾的是我无法访问IE来测试它。因此,当使用for..in迭代属性时,IE还会保留排序(6/7以后),假设我们不从对象中删除任何内容吗?

此外,for..in never建议用于迭代数组,原因完全相同,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是否支持对于任何版本的插入顺序的对象迭代?是否有其他浏览器也不支持此功能。

3 个答案:

答案 0 :(得分:4)

IE 5.5-6-7-8上的

Tests显示了完全相同的行为。这些属性是有序的。已删除的属性保持其位置。

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

也许我错过了什么,但重点是什么?与更强大的代码相比,可以节省一些字符,与第三方广告,跟踪系统,小部件等运行良好。

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

答案 1 :(得分:3)

浏览器不仅仅是这里提到的浏览器:还有其他当前浏览器和未来的浏览器。在迭代对象的属性时,它们都没有义务实现任何排序,并且不会持续很长时间,因为最近的ECMAScript 5没有指定排序。实际上,Chrome development team is refusing to change its implementation尽管声称要求使其符合其他浏览器。

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

答案 2 :(得分:1)

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