iteration order for Object

Charles Kendrick charles at isomorphic.com
Thu Mar 10 15:48:51 PST 2011


I believe it is very very important that the ECMAScript standard specify that when a new Object
is created, for..in iteration traverses properties in the order they are added, regardless of 
whether the properties are numeric or not.

The primary reason is that I think it makes ECMAScript a much more expressive and usable 
language, and results in better performance in real-world applications.

Secondarily, the fact that the Chrome browser has deviated from this de-facto standard is 
creating a small crisis for site owners and web application developers, and it will get much 
much worse if any other browser vendors follow suit.

This seems to have been most recently discussed in 2009 with inconclusive results.

	https://mail.mozilla.org/htdig/es-discuss/2009-October/010060.html

I have summarized the argument for this feature below - this argument has swayed others who 
were initially opposed.

I'm hoping we can get quick consensus that *specifically Object iteration order* should be 
preserved, without getting too bogged down in the details of specifying exactly what happens 
for Arrays, prototype chains, etc.  Major stakeholder agreement on this one aspect should be 
enough to prevent any other vendors from shipping browsers that break sites, and get the Chrome 
bug for this re-instated.


== Expressiveness and Performance argument

A very common use case where order preservation is desirable is providing the set of options
for a drop-down list in JavaScript.  Essentially all Ajax widget kits have such an API, and
usage is generally:

    selectControl.setOptions({
       storedValue1 : "displayValue1",
       storedValue2 : "displayValue2",
       storedValue3 : "displayValue3"
    })

Here are some examples of what alternatives might look like - all of them are far, far worse:

#1 Parallel arrays:

    selectControl.setOptions(
       ["storedValue1",
        "storedValue2",
        "storedValue3"],
       ["displayValue1",
        "displayValue2",
        "displayValue3"]
    })

- this is awkward and unnatural, and doesn't correspond to how a list of options is specified
   in HTML

- involves *double* the number of allocations / GC-tracked objects (6 strings and 2 Arrays
   vs one Object and 3 Strings - slots don't GC)

- replacing a key/value pair requires a linear (0(n)) search unless secondary indexing
   approaches are used, which requires yet more allocation both to build and maintain the index,
   as well as a level of sophistication not typical for a scripting language user


#2 Array of Objects

     selectControl.setOptions([
         {value: "storedValue1", text: "displayValue1"},
         {value: "storedValue2", text: "displayValue2"},
         {value: "storedValue3", text: "displayValue3"}
     ]);

- verbose and redundant code - reiterates "value" and "text" once per entry

- much worse Object allocation than #1 (which was already bad): one Object + 2 Strings per
   property

- same linear search / extra allocation / developer sophistication issue as #1



#3 Array of Arrays

     selectControl.setOptions([
         ["storedValue1", "displayValue1"],
         ["storedValue2", "displayValue2"],
         ["storedValue3", "displayValue3"]
     ]);

- verbose, finger-spraining punctuation density

- much worse Object allocation than #1 (which was already bad): one Array + 2 Strings per
   property

- same linear search / extra allocation / developer sophistication issue as #1




In a nutshell, dropping order preservation results in:

1. less expressive code

2. more bytes on the wire (both in code-as-such and JSON)

3. degraded application performance via increased allocations and the overhead of implementing
    order-preserving behavior in JavaScript


== Historical behavior argument

All browsers that have ever had non-negligible market share have implemented order-preserving
Objects - until Chrome 6.

Like many universally consistent, obviously beneficial behaviors, many developers relied on it
assuming eventual standardization.

Thousands of sites and applications are broken by Chrome's decision to drop the
order-preserving behavior.  There is a bug against Chrome's V8 engine (currently marked
"WorkingAsIntended"):

       http://code.google.com/p/v8/issues/detail?id=164

People can "star" issues to be notified of changes in status or discussion.  This issue has by
far more stars than the most-starred Feature Request (E4X support), more than double the stars
of the runner-up, and more stars than roughly the top 20 confirmed bugs combined.

And this does not consider all the stars on other versions of this issue that were closed as
duplicates.

Various arguments have gone back and forth on whether Chrome should fix this bug without
waiting for standardization, but not a single person has indicated that they would prefer that
Object does not preserve order.

In a nutshell, there is overwhelming support for adding this behavior to the standard, and
still time to avoid all the wasted effort of changing all these sites and applications.  Very
few non-order-preserving browsers exist in wild, and the behavior is limited to browsers that
are updated very frequently or even automatically.


== Objections and counter-arguments

1. Array for..in iteration order has always been inconsistent across browsers

Yes, this is true.  I am proposing only that Object preserves insertion order, not Array.

No developers or sites rely on Array for..in iteration order, since it was never consistent.

If Array for..in iteration continues to be unordered, any developer that cares about the tiny
performance difference can use an Array to store non-numeric property/value pairs.


2. Not preserving insertion order allows JavaScript to run faster

It can't be a very large optimization, since Safari and Firefox continue to challenge Chrome's
performance while maintaining in-order iteration.  And if it's only a small optimization, then
obviously it's completely dwarfed by the application-level penalties incurred re-implementing
this key behavior in JavaScript.


3. It's good enough to preserve order for non-numeric keys only

This is an abysmal compromise, with the worst traits of each alternative.  It requires browser 
vendors to implement order preservation, such that we don't get the minor optimization that's 
possible from not preserving order at all.  At the same time, it requires that applications and 
frameworks deal with lack of order for numeric keys, which are very common: in the use case of 
mapping stored to displayed values, stored values are very often numeric.

It's also just bad design.  It surprising and counter-intuitive that numeric keys are treated 
differently from non-numeric.  The reality is that an implementation detail of Array is 
bleeding through to Object.






More information about the es-discuss mailing list