views:

259

answers:

3

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.

+1  A: 

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.

maerics
Thanks @maerics - that is really useful. It will be nice if IE 6/7 showed the same behavior.
Anurag
+2  A: 

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.

galambalazs
@galambalazs - Thanks for the exhaustive IE tests. As long as we are aware of the behavior of different browsers out there, I don't see how this can not live with 3rd party code. As to why it's important, there are many use-cases for having an ordered associative array. You can checkout this question's [comment thread](http://stackoverflow.com/questions/2886681/merging-javascript-arrays-for-json) for more details. We can surely write our own `OrderedMap` or `LinkedMap` implementations if a more robust functionality along with deleting is needed, but for read-only lists, it just works :)
Anurag
+1  A: 

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.

Tim Down