Does JavaScript guarantee object property order? [Answered]

Query:

If I create an object like this:

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";

Will the resulting object always look like this?

{ prop1 : "Foo", prop2 : "Bar" }

That is, will the properties be in the same order that I added them?

Does JavaScript guarantee object property order? Answer #1:

YES (for non-integer keys).

Most Browsers iterate object properties as:

  1. Integer keys in ascending order (and strings like “1” that parse as ints)
  2. String keys, in insertion order (ES2015 guarantees this and all browsers comply)
  3. Symbol names, in insertion order (ES2015 guarantees this and all browsers comply)

Some older browsers combine categories #1 and #2, iterating all keys in insertion order. If your keys might parse as integers, it’s best not to rely on any specific iteration order.

Current Language Spec (since ES2015) insertion order is preserved, except in the case of keys that parse as integers (eg “7” or “99”), where behavior varies between browsers. For example, Chrome/V8 does not respect insertion order when the keys are parse as numeric.

Old Language Spec (before ES2015): Iteration order was technically undefined, but all major browsers complied with the ES2015 behavior.

Note that the ES2015 behavior was a good example of the language spec being driven by existing behavior, and not the other way round. To get a deeper sense of that backwards-compatibility mindset, see http://code.google.com/p/v8/issues/detail?id=164, a Chrome bug that covers in detail the design decisions behind Chrome’s iteration order behavior. Per one of the (rather opinionated) comments on that bug report:

Standards always follow implementations, that’s where XHR came from, and Google does the same thing by implementing Gears and then embracing equivalent HTML5 functionality. The right fix is to have ECMA formally incorporate the de-facto standard behavior into the next rev of the spec.

Answer #2:

The iteration order for objects follows a certain set of rules since ES2015, but it does not (always) follow the insertion order. Simply put, the iteration order is a combination of the insertion order for strings keys, and ascending order for number-like keys:

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }

Using an array or a Map object can be a better way to achieve this. Map shares some similarities with Object and guarantees the keys to be iterated in order of insertion, without exception:

The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion. (Note that in the ECMAScript 2015 spec objects do preserve creation order for string and Symbol keys, so traversal of an object with ie only string keys would yield keys in order of insertion)

As a note, properties order in objects weren’t guaranteed at all before ES2015. Definition of an Object from ECMAScript Third Edition (pdf):

4.3.3 Object

An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method.

Answer #3:

Property order in normal Objects is a complex subject in JavaScript.

While in ES5 explicitly no order has been specified, ES2015 defined an order in certain cases, and successive changes to the specification since have increasingly defined the order (even, as of ES2020, the for-in loop’s order). Given is the following object:

const o = Object.create(null, {
  m: {value: function() {}, enumerable: true},
  "2": {value: "2", enumerable: true},
  "b": {value: "b", enumerable: true},
  0: {value: 0, enumerable: true},
  [Symbol()]: {value: "sym", enumerable: true},
  "1": {value: "1", enumerable: true},
  "a": {value: "a", enumerable: true},
});

This results in the following order (in certain cases):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}

The order for “own” (non-inherited) properties is:

  1. Integer-like keys in ascending order
  2. String keys in insertion order
  3. Symbols in insertion order

Thus, there are three segments, which may alter the insertion order (as happened in the example). And integer-like keys don’t stick to the insertion order at all.

In ES2015, only certain methods followed the order:

  • Object.assign
  • Object.defineProperties
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys
  • JSON.parse
  • JSON.stringify

As of ES2020, all others do (some in specs between ES2015 and ES2020, others in ES2020), which includes:

  • Object.keys, Object.entries, Object.values, …
  • for..in

The most difficult to nail down was for-in because, uniquely, it includes inherited properties. That was done (in all but edge cases) in ES2020. The following list from the linked (now completed) proposal provides the edge cases where the order is not specified:

  • Neither the object being iterated nor anything in its prototype chain is a proxy, typed array, module namespace object, or host exotic object.
  • Neither the object nor anything in its prototype chain has its prototype change during iteration.
  • Neither the object nor anything in its prototype chain has a property deleted during iteration.
  • Nothing in the object’s prototype chain has a property added during iteration.
  • No property of the object or anything in its prototype chain has its enumerability change during iteration.
  • No non-enumerable property shadows an enumerable one.

Conclusion: Even in ES2015 you shouldn’t rely on the property order of normal objects in JavaScript. It is prone to errors. If you need ordered named pairs, use Map instead, which purely uses insertion order. If you just need order, use an array or Set (which also uses purely insertion order).

Answer #4:

This whole answer is in the context of spec compliance, not what any engine does at a particular moment or historically.

Generally, no

The actual question is very vague.

will the properties be in the same order that I added them

In what context?

The answer is: it depends on a number of factors. In general, no.

Sometimes, yes

Here is where you can count on property key order for plain Objects:

  • ES2015 compliant engine
  • Own properties
  • Object.getOwnPropertyNames()Reflect.ownKeys()Object.getOwnPropertySymbols(O)

In all cases these methods include non-enumerable property keys and order keys as specified by [[OwnPropertyKeys]] (see below). They differ in the type of key values they include (String and / or Symbol). In this context String includes integer values.

Object.getOwnPropertyNames(O)

Returns O‘s own String-keyed properties (property names).

Reflect.ownKeys(O)

Returns O‘s own String– and Symbol-keyed properties.

Object.getOwnPropertySymbols(O)

Returns O‘s own Symbol-keyed properties.

[[OwnPropertyKeys]]

The order is essentially: integer-like Strings in ascending order, non-integer-like Strings in creation order, Symbols in creation order. Depending which function invokes this, some of these types may not be included.

The specific language is that keys are returned in the following order:

  1. … each own property key P of O [the object being iterated] that is an integer index, in ascending numeric index order
  2. … each own property key P of O that is a String but is not an integer index, in property creation order
  3. … each own property key P of O that is a Symbol, in property creation order

Map

If you’re interested in ordered maps you should consider using the Map type introduced in ES2015 instead of plain Objects.

Hope you learned something from this post.

Follow Programming Articles for more!

About ᴾᴿᴼᵍʳᵃᵐᵐᵉʳ

Linux and Python enthusiast, in love with open source since 2014, Writer at programming-articles.com, India.

View all posts by ᴾᴿᴼᵍʳᵃᵐᵐᵉʳ →