Does ES6 introduce a well-defined order of enumeration for object properties?
var o = { '1': 1, 'a': 2, 'b': 3 } Object.keys(o); // ["1", "a", "b"] - is this ordering guaranteed by ES6? for(let k in o) { console.log(k); } // 1 2 3 - is this ordering guaranteed by ES6?
Advertisement
Answer
Note: As of ES2020, even older operations like for-in
and Object.keys
are required to follow property order. That doesn’t change the fact that using property order for fundamental program logic probably isn’t a good idea, since the order for non-integer-index properties depends on when the properties were created.
Answer for ES2015-ES2019:
For for-in
, Object.keys
, and JSON.stringify
: No.
For some other operations: Yes, usually.
While ES6 / ES2015 adds property order, it does not require for-in
, Object.keys
, or JSON.stringify
to follow that order, due to legacy compatibility concerns.
for-in
loops iterate according to [[Enumerate]], which is defined as (emphasis mine):
When the [[Enumerate]] internal method of O is called the following steps are taken:
Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of enumerable properties of O. The Iterator object must inherit from %IteratorPrototype% (25.1.2). The mechanics and order of enumerating the properties is not specified but must conform to the rules specified below [1].
ES7 / ES2016 removes the [[Enumerate]] internal method and instead uses the abstract operation EnumerateObjectProperties, but just like [[Enumerate]] it doesn’t specify any order.
And also see this quote from Object.keys
:
If an implementation defines a specific order of enumeration for the for-in statement, […]
That means implementations are NOT required to define a specific order of enumeration. This has been confirmed by Allen Wirfs-Brock, Project Editor of the ECMAScript 2015 Language Specification, in a post made after the specification was complete.
Other operations, like Object.getOwnPropertyNames
, Object.getOwnPropertySymbols
, Object.defineProperties
, and Reflect.ownKeys
do follow the following order for ordinary objects:
- Integer indices (if applicable), in ascending order.
- Other string keys (if applicable), in property creation order.
- Symbol keys (if applicable), in property creation order.
This behavior is defined in the [[OwnPropertyKeys]] internal method. But certain exotic objects define that internal method slightly differently. For example, a Proxy’s ownKeys
trap may return an array in any order:
console.log(Reflect.ownKeys(new Proxy({}, { ownKeys: () => ['3','1','2'] }))); // ['3','1','2'], the integer indices are not sorted!
[1] Below it says:
[[Enumerate]] must obtain the own property keys of the target object as if by calling its [[OwnPropertyKeys]] internal method.
And the order of [[OwnPropertyKeys]] is well-defined. But don’t let that confuse you: that “as if” only means “the same properties”, not “the same order”.
This can be seen in EnumerableOwnNames, which uses [[OwnPropertyKeys]] to get the properties, and then it orders them
in the same relative order as would be produced by the Iterator that would be returned if the [[Enumerate]] internal method was invoked
If [[Enumerate]] were required to iterate with the same order as [[OwnPropertyKeys]], there wouldn’t be any need to reorder.