Sorting properties in Javascript is broken

This has to do with the way they keys of an object are traversed.

According to the ES6 specifications it should be:

9.1.12 [[OwnPropertyKeys]] ( )

When the [[OwnPropertyKeys]] internal method of O is called the following steps are taken:

    Let keys be a new empty List.
    For each own property key P of O that is an integer index, in ascending numeric index order
        Add P as the last element of keys.
    For each own property key P of O that is a String but is not an integer index, in property creation order
        Add P as the last element of keys.
    For each own property key P of O that is a Symbol, in property creation order
        Add P as the last element of keys.
    Return keys.

http://www.ecma-international.org/ecma-262/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys

That means if the value of a key stays the same if converted to an unsigned 53 bit number and back it is treated as an integer index which gets sorted in ascending numeric order.

If this fails it's treated as a string key, which are ordered the way they were added to the object.

The catch here is that all major browsers don't follow this specification yet and use an array index instead which is limited to a positive number up to 2^32-1. So anything above that limit is a string key actually.


This is expected. Per the specification, the method that iterates over properties, OrdinaryOwnPropertyKeys, does:

  1. For each own property key P of O that is an array index, in ascending numeric index order, do

    a. Add P as the last element of keys.

  2. For each own property key P of O that is a String but is not an array index, in ascending chronological order of property creation, do

    a. Add P as the last element of keys.

The ascending numeric order only applies for properties which are array indicies.

So, what is an "array index"? Look it up::

An integer index is a String-valued property key that is a canonical numeric String (see 7.1.21) and whose numeric value is either +0 or a positive integer ≤ 2^53 - 1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 2^32 - 1.

So, numeric properties that are greater than 2^32 are not array indicies, and therefore iterated in order of property creation. However, numeric properties that are less than 2^32 are array indicies, and are iterated over in ascending numeric order.

So, for example:

1: Array index, will be iterated over numerically

10: Array index, will be iterated over numerically

4294968111: Greater than 2 ** 32, will be iterated over after array indicies are finished, in property creation order

9999999999999: Greater than 2 ** 32, will be iterated over after array indicies are finished, in property creation order

Also, keep in mind that, contrary to popular belief, property iteration order is guaranteed by the specification as well, thanks to the for-in iteration proposal which is stage 4.

Tags:

Javascript