Property order is predictable in JavaScript objects since ES2015

3 min read

This post is part of my Today I learned series in which I share all my learnings regarding web development.

Today's learning surprised me!

I was reading Twitter and came across a thread started by Sebastian McKenzie. In this thread, he shared a React snippet that would rely on the order of specific properties in an object.

styles({
  backgroundBlue: true,
  backgroundRed: true
});

In the snippet above the background would be red as "it appears last". If you do JavaScript for a while, you might immediately think – Wait, what!?!

In this post, I mainly share snippets and facts because I don't want to repeat the content of resources listed at the end.

# The common misconception – "the order of JavaScript properties cannot be guaranteed"

When you started writing JavaScript a few years ago, you might have heard the statement that the order of properties in JS objects is not predictable. I never came across an odd and unusual order of properties, but I always followed the rule "never rely on property order".

# The internal ownPropertyKeys method

It turns out – since ES2015 there are methods that are based on specific rules defining the order of properties, and apart from one particular case the order is chronological. The order of properties in an object depends on the type of the included properties and their values.

Looking at the spec the rules are defined in the internal "ownPropertyKeys" method. Which is used for example by fairly new methods Object.getOwnPropertyNames and Reflect.ownKeys.

What is interesting is that there was a spec change of e.g. Object.keys from ES5 to ES6. The ES6 spec defines that Object.keys also relies on ownPropertyKeys which makes it predictable in today's browsers, too.

This also means that you have to be careful with this method, though, and shouldn't rely on a predictable order using Object.keys because the results may vary depending on the browser implementation.

But enough of the theory: let's have a look at the defined property order for methods implementing ownPropertyKeys.

# 1. Integer Indices

All properties that are integer indices appear first in the overall object property order and are sorted numerically.

const objWithIndices = {
  23: 23,
  '1': 1,
  1000: 1000
};

console.log(Reflect.ownKeys(objWithIndices));
// [1, 23, 1000]
// ☝️ following numeric order

# 2. Strings (that are no integers)

Properties that do not count to integer indices and are not of type Symbol come next and follow chronological order.

const objWithStrings = {
  'bar': 'bar',
  '01': '01'
};

objWithStrings.last = 'last';
objWithStrings['veryLast'] = 'veryLast';

console.log(Reflect.ownKeys(objWithStrings));
// ['bar', '01', 'last', 'veryLast']
// ☝️ following chronological order

# 3. Symbols

At last, Symbols follow a chronological order, too.

const objWithSymbols = {
  [Symbol('first')]: 'first',
  [Symbol('second')]: 'second'
};

objWithSymbols[Symbol('last')] = 'last';

console.log(Reflect.ownKeys(objWithSymbols));
// [Symbol(first), Symbol(second), Symbol(last)]
// ☝️ following chronological order

# All together

When you combine these rules, you’ll see that integers are always in the “front row” of object properties followed by strings and Symbols. Moreover, we can control the order of the string and Symbol properties because they’re chronological!

const obj = {
  '2': 'integer: 2',
  'foo': 'string: foo',
  '01': 'string: 01',
  1: 'integer: 1',
  [Symbol('first')]: 'symbol: first'
};

obj['0'] = '0';
obj[Symbol('last')] = 'symbol: last';
obj['veryLast'] = 'string: very last';

console.log(Reflect.ownKeys(obj));
// [ "0", "1", "2", "foo", "01", "veryLast", Symbol(first), Symbol(last) ]
// -> 1. integers in numeric order
// -> 2. strings in chronological order
// -> 3. Symbols in chronological order

Edited: As Malgosia Stepniak pointed out "own property order" is only fully supported in modern browsers and not in e.g. IE.

# Additional resources

Thanks to Axel who wrote about this three years ago already. :)

Load time