Member Ordering

# Brendan Eich (17 years ago)

On Jun 3, 2008, at 9:25 AM, Douglas Crockford wrote:

ES3 says that objects are unordered with respect to the sequence
produced by for...in. Nevertheless, many web developers have an expectation
that the sequence should reproduce the insertion order. That implied order
is strictly complied with in all browsers except Opera which can optimize
numeric sequences.

We like the Opera optimization. Web developers don't seem to notice
that it diverges from common practice, except that for in on arrays seems
to work correctly in cases that seem to be confused on other browsers.

Confused in other browsers only if one creates the array's indexed
properties in other than index order. That's rare, so it's not a huge
hardship and I hope we can do what you propose, even though it's an
incompatible change from past releases of most browsers. From a
Firefox 2's JavaScript 1.7, in the SpiderMonkey shell:

js> a = []

js> a[3]=3

3 js> a[2]=2

2 js> a[1]=1

1 js> a[0]=0

0 js> for(i in a)print(i)

3 2 1 0

We are also reluctant to slap Opera for having produced the best
implementation of this feature in a way that fully complies with the current
standard.

You would not be slapping only Opera. Firefox 3's JavaScript 1.8
matches it:

js> a = [] [] js> a[3]=3

3 js> a[2]=2

2 js> a[1]=1

1 js> a[0]=0

0 js> for(i in a) print(i)

0 1 2 3

but only for dense arrays: length <= 32 or load factor >= .25 -- and

no ad-hoc named, direct properties. I hear Opera has similar
restrictions, but I haven't tested.

There are other hard cases:

js> a = []

js> a[3]=3

3 js> a[2]=2

2 js> Array.prototype[1] = 1

1 js> Object.prototype[0] = 0

0 js> for (i in a) print(i)

2 3 1 0

We want to better specify the ordering of for...in because the
developer community has expectations. We are reluctant to impose a particular implementation of objects, but we do like that Opera's optimization
seems to best match expectations in a way that is the least surprising, and
possibly the best performing.

How would the other three feel about having to adopt Opera's
convention?

We need an exact spec of what Opera does to agree, or better: we need
a spec that matches Opera and Firefox 3 for the easy case of dense
arrays with no prototype indexed properties and no ad-hoc named
properties.

I'm in favor of for-in using index order for arrays, provided we can
get the hard cases "right" for some reasonable definition of "right".
We'd adapt future Mozilla releases to match.

# Lars Hansen (17 years ago)

(Bringing back cross-posting to es4-discuss.)

Why should Array objects be treated specially? I can't speak to the current internals of Opera, but I know that in the past it segregated named and numbered properties, independently of the type of the Object, and enumeration of properties did not depend on the type of object. That seems like a feature.

ES4 'vector' won't behave anything like this. The current spec says that the default iterator for vector enumerates properties in the range 0 .. length-1 in that order and skips prototype properties and all named properties.
Programmers who want to enumerate those have to create a custom Enumerator object.

We also believe that the right thing is to provide some facilities on Array objects so that for-in loops can be used to iterate across numbered properties only, eg, a 'range' property that delivers a suitable iterator. But some details remain unsettled.

It's possible that the uniform thing to do would be for enumeration to work the same on vector as on Array, and for there to be a 'range' property on vector to deliver the sane iterator. But IMO the Array behavior is broken and would be changed, were it not for backward compatibility concerns.

# Pratap Lakshman (VJ#SDK) (17 years ago)

JScript too (starting IE8 Beta1) has an internal representation for arrays that segregates named and numbered properties. The current spec does not mandate an ordering (also, refer section 3.6 from the Deviations document on the wiki), and it appears that various implementations have been able to use that to optimize their internal representations.

For e.g. I see different outputs from the various implementations for the following test case: <script>

Array.prototype.x = "x"; Array.prototype.y = "y";

arr = new Array(); arr[0] = 0; arr[1] = 1; arr.exp = "exp"; arr[2] = 2;

arr.x = "shadowX"; arr.y = "shadowY"

for (t in arr) { document.write("arr[" + t + "] = " + arr[t] + ", "); }

/* IE8 B1: arr[0] = 0, arr[1] = 1, arr[exp] = exp, arr[2] = 2, arr[x] = shadowX, arr[y] = shadowY, FF 3: arr[0] = 0, arr[1] = 1, arr[exp] = exp, arr[2] = 2, arr[x] = shadowX, arr[y] = shadowY, Opera 9.02: arr[0] = 0, arr[1] = 1, arr[2] = 2, arr[exp] = exp, arr[x] = shadowX, arr[y] = shadowY, Safari 3.0: arr[0] = 0, arr[1] = 1, arr[2] = 2, arr[exp] = exp, arr[x] = shadowX, arr[y] = shadowY, */ </script>

This may not be easy to pin down in the spec. Perhaps, we should provide other means to mitigate the issues arising out of not having a mandated enumeration order.

pratap

# Douglas Crockford (17 years ago)

This is a surprisingly complicated subject. I do not think we can bring this to a successful conclusion and still meet the deadlines for ES3.1. So we should defer this issue to ES3.14, which will give us an additional 6 months to work out Object's Rules of Order.

In the meantime, I think ES3.1 should retain ES3's unordered language. There isn't time to do anything else.

The most compelling use case for a standard ordering is testing. ES3.1 will provide some convenience there. The Object.keys(object, fast) function will produce an array of strings from the names of the object's own, enumerable keys. When the fast parameter is falsy, the keys will be sorted. The array of keys can be used with the new array methods to produce a string that can be easily tested for success.

# Brendan Eich (17 years ago)

Using Lars's reply to respond to Mark's proposal, since it was not
cross-posted. Mark's proposal text, one level of cite-quotation
removed, first:

-----Original Message----- From: Mark S. Miller [mailto:erights at google.com] Sent: 4. juni 2008 17:59 To: Douglas Crockford Cc: Lars Hansen; es3.x-discuss at mozilla.org; brendan at mozilla.org Subject: Re: Member Ordering

On Wed, Jun 4, 2008 at 8:42 AM, Douglas Crockford <douglas at crockford.com> wrote:

That is indeed discouraging. Perhaps ES3 did the right thing by leaving the order unspecified.

No. Please specify some deterministic order. I don't much care which. But leaving it unspecified complicates testing unnecessarily. Also, please see www.cs.berkeley.edu/~amettler/purecomp.pdf (draft paper) for further benefits that can only be realized after removing such unavoidable sources of hidden non-determinism. (Brendan, I've added you to the cc to make sure you see this paper. It makes another interesting bridge between information-flow and ocaps.)

(Thanks!)

I agree that we need to specify more than what ES1-3 specified.

The de-facto standard of insertion order remains mandatory, as far as
I can tell, for most objects and named properties. You can't make a
web-compatible browser without it.

But index-named properties, specifically in Array instances, could be
treated differently -- maybe. We'll find out with very-large-scale
Firefox 3 testing how often any web content cares that index order is
not necessarily insertion order.

A proposal:

  • For direct instances of Array: Use the Opera ordering for the own properties. Then the normal prototype-following insertion order for ascending the superclass chain. Note that Array.prototype is not an instance of Array, and so should enumerate by insertion order.

Array.prototype is an Array instance, but with a [[Prototype]]
linking to Object.prototype instead of to Array.prototype (as all
other Array instances have) -- see ECMA-262 Edition 3 15.4.4, first
two paragraphs:

15.4.4 Properties of the Array Prototype Object

The value of the internal [[Prototype]] property of the Array
prototype object is the Object prototype object (section 15.2.3.1).

The Array prototype object is itself an array; its [[Class]] is
"Array", and it has a length property (whose initial value is +0) and
the special internal [[Put]] method described in section 15.4.5.1.

On the other hand, Mark's proposal happens to match Firefox 3
(SpiderMonkey JS1.8) because Array.prototype is not "dense" -- it has
a bunch of named properties (to wit, the standard methods plus some
extensions). But this is just a bug.

I like that the proposal does not generalize to indexed properties in
any object. That seems better than obligating all implementations to
segregate named from indexed properties in their property-map
implementations. Array, Vector, other array-likes can opt in. ES4
should specify which ones do.