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. :)
Top comments (0)