当排序很重要时,使用 for..in 迭代 JavaScript 对象属性和数组
这是一个古老的问题,我知道通常的问题在需要任何排序时不使用 for..in
甚至对象的原因,但我最近从 MDC 删除运算符。
跨浏览器问题
虽然 ECMAScript 使对象的迭代顺序依赖于实现,但似乎所有主要浏览器都支持基于最早添加的属性的迭代顺序(至少对于不在原型上的属性)。然而,就 Internet Explorer 而言,当对某个属性使用 delete 时,会产生一些令人困惑的行为,从而阻止其他浏览器将对象文字等简单对象用作有序关联数组。在资源管理器中,虽然属性值确实设置为未定义,但如果稍后添加回具有相同名称的属性,则该属性将在其旧位置进行迭代,而不是像人们在获得后所期望的那样在迭代序列的末尾进行迭代。删除该属性,然后将其添加回来。
因此,如果您想在跨浏览器环境中模拟有序关联数组,则必须使用两个单独的数组(一个用于键,另一个用于值),或者构建一个单一属性的数组物体等
大多数主要浏览器 - Chrome、Safari、Firefox 和 Opera 按插入顺序枚举对象属性。这是一个快速测试来确认这一点。它可能不适用于 IE,但不幸的是我无法访问 IE 来测试它。那么,假设我们不从对象中删除任何内容,那么在使用 for..in
迭代属性时,IE 是否也会保留顺序(6/7 以后)?
另外, for..in
是
有两种解决方案:
使用 hasOwnProperty
检查属性是否属于该对象
for(var index in object) {
if(object.hasOwnProperty(index)) {
..
}
}
使用新的 ES5 语法定义对象的属性并使它们不可枚举。这假设您可以完全控制基本原型的扩展,并且没有第 3 方代码直接在原型上添加任何内容。
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 是否支持任何版本的按插入顺序进行对象迭代?还有其他浏览器也不支持此功能吗?
This has been an age old question and I am aware of the usual reasons for not using for..in
or perhaps even objects when any sort of ordering is needed, but I recently came across this article from MDC on the delete operator.
Cross-browser issues
Although ECMAScript makes iteration order of objects implementation-dependent, it may appear that all major browsers support an iteration order based on the earliest added property coming first (at least for properties not on the prototype). However, in the case of Internet Explorer, when one uses delete on a property, some confusing behavior results, preventing other browsers from using simple objects like object literals as ordered associative arrays. In Explorer, while the property value is indeed set to undefined, if one later adds back a property with the same name, the property will be iterated in its old position--not at the end of the iteration sequence as one might expect after having deleted the property and then added it back.
So if you want to simulate an ordered associative array in a cross-browser environment, you are forced to either use two separate arrays (one for the keys and the other for the values), or build an array of single-property objects, etc.
It appears that most major browsers - Chrome, Safari, Firefox, and Opera enumerate object properties in insertion order. This is a quick test to confirm that. It may not work on IE, but I don't have access to IE to test this out unfortunately. So does IE preserve ordering as well (6/7 onwards) when iterating through properties using for..in
assuming we don't delete anything from the object?
Also, for..in
is never recommended for iterating through arrays for the exact same reasons, and the fact that Array.prototype
is more commonly tampered with than Object.prototype
. However, if properties were actually enumerated in insertion order, then I suppose for..in
can be used to loop over arrays as well, with the following gotcha - Properties listed on Array.prototype
will be enumerated as well.
There are two solutions to this:
Check if property belongs to the object using hasOwnProperty
for(var index in object) {
if(object.hasOwnProperty(index)) {
..
}
}
Use the new ES5 syntax for defining properties on objects and make them non-enumerable. This assumes that you have full control over the extension to base prototypes, and no 3rd party code adds anything on the prototype directly.
Object.defineProperty(Array.prototype, "size", {
enumerable: false,
configurable: false,
get: function() { return this.length; },
set: undefined
});
If all the above conditions are met, and unless I am missing something crucial, for..in
can be safely used for array iteration as well:
var values = [1, 2, 3];
for(var i in values) {
values[i];
}
So I guess it's back to the original question. Disregarding what the spec says, does IE support object iteration in insertion order, for any versions? Are there any other browsers that don't support this as well.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在 IE 5.5-6-7-8 上进行的测试显示了完全相同的行为。属性是有序的。已删除的属性保持其位置。
但由于它是非标准行为,它可能会破坏 IE9 或任何浏览器的下一个版本......
也许我错过了一些东西,但有什么意义呢?与更强大的代码相比,可以节省一些字符,并且与第三方广告、跟踪系统、小部件等运行良好?
更不用说支持移动浏览器和市场份额很小的桌面浏览器了。
Tests on IE 5.5-6-7-8 showed the exact same behavior. The properties are ordered. The deleted property keeps its position.
But as it is non-standard behavior it can break with IE9, or any browser's next version...
And maybe I've missed something but what's the point? Saving some characters in comparison to a more robust code, that runs well with 3rd party ads, tracking systems, widgets etc.?
Let alone supporting mobile browsers, and the desktop ones with tiny market share.
除了这里提到的浏览器之外,还有更多的浏览器:还有其他当前的浏览器和未来的浏览器。在迭代对象的属性时,它们都没有义务实现任何排序,并且在很长一段时间内都不会这样做,因为最近的 ECMAScript 5 指定了无排序。事实上,Chrome 开发团队拒绝更改其实现尽管有人要求它与其他浏览器兼容。
任何基于当前少数浏览器的观察行为的假设充其量都是靠不住的。正如所观察到的,并非所有当前浏览器的行为都相同,未来的浏览器可能会选择不符合您的假设,并且完全有权这样做。因此,我强烈建议不要依赖任何代码中的任何特定顺序。
There are more browsers than just those mentioned here: there are other current browsers and future browsers. None of them are obliged to implement any ordering when iterating over the properties of an object, and will not be for a long time, since the recent ECMAScript 5 specifies no ordering. Indeed, the Chrome development team is refusing to change its implementation despite vocal requests to make it conform with other browsers.
Any assumptions based on observed behaviour of a handful of current browsers is shaky at best; as observed, not all current browsers behave the same and future browsers may choose not to conform to your assumptions and have every right to do so. Therefore I strongly recommend not relying on any particular ordering in any of your code.
在 WinXP pro SP2 上的 IE8 上运行测试证实了 MDC 文章。 IE8 按照声明的顺序迭代成员;如果删除现有属性然后重新分配,则保留其原始迭代位置。其他浏览器(我验证了 Chrome 5 和 Firefox 3)将重新分配的属性放在迭代顺序的末尾。
Running your test on IE8 on WinXP pro SP2 confirms the MDC article. IE8 iterates the members in the order they are declared; if an existing property is deleted and then later reassigned then its original iteration position is maintained. Other browsers (I verified Chrome 5 and Firefox 3) place the reassigned property at the end of the iteration order.