DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Anthony Frehner
Anthony Frehner

Posted on • Updated on

The Order of Javascript Object Keys

Trivia time! What does the following array look like?

Object.keys({
  2: true, 
  1: true,
  '00': true,
  'b': true,
  'a': true,
  '3': true,
})
Enter fullscreen mode Exit fullscreen mode

Answer (click to see)
[ "1", "2", "3", "00", "b", "a" ]
Enter fullscreen mode Exit fullscreen mode

So what exactly is going on?? Here are the ordering rules:

  1. Numbers are ordered first, and they are ordered within themselves from smallest to largest as long as they are >=0 (see below for more details)
  2. Strings come second, and they are ordered within themselves by insertion order
  3. Symbols come last, and they are ordered within themselves by insertion order (note that we didn't use symbols in this example)

But wait, why did '3' come before '00' if strings are ordered within themselves by insertion order?

Well, turns out that JS will see if your string can be converted to a number - if it can, then it will order it with the numbers and not the strings.

And what about '00'? Apparently it converts it to a new number, then does something similar to toString() on new number, and compares that new string with the original string.

If they match, then it can be lumped in with the numbers. If it doesn't match, then it's a string.

const originalString = '00'
const stringToNumber = Number(originalString)
const matchesOriginalString = stringToNumber.toString() === originalString // false: '0' !== '00'
Enter fullscreen mode Exit fullscreen mode

Pretty clear, huh? :P

Thanks to this article for helping https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/


Here's the wording from the spec:

Numbers

For each own property key P of O such that P is an array index, in ascending numeric index order, Add P as the last element of keys.

i.e. insert numerical keys first in ascending order

Strings

For each own property key P of O such that Type(P) is String and P is not an array index, in ascending chronological order of property creation, Add P as the last element of keys.

i.e. insert string keys in order of creation as long as they're not an array index. So what's that?

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 ≀ 253 - 1. An array index is an integer index whose numeric value i is in the range +0 ≀ i < 232 - 1.

source for array index

i.e. a string key that is a canonical numeric string and greater than +0

so... what's a canonical numeric string?

[A canonical numeric string is an] argument converted to a Number value if it is a String representation of a Number that would be produced by ToString, or the string "-0".

source for canonical numeric string
i.e. if that string is the same as any number that is toString()

Symbols

For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, Add P as the last element of keys.

i.e. insert symbol keys in order of creation

Top comments (4)

Collapse
 
crosseye profile image
Scott Sauyet

I was looking to link to a description of Object iteration order, and this is one of the first search results. But it is not really correct, as can be seen with this variation:

Object.keys({
  2: true, 
  '1': true,
  'b': true,
  '00': true,
  'a': true,
  3: true,
}) //=> ['1', '2', '3', 'b', '00', 'a']
Enter fullscreen mode Exit fullscreen mode

Note that '1' still comes before '3', even though '1' was specified as a String and 3 as a Number. Also 'b' comes before '00' because of insertion order. It has nothing to do with the numeric value of '00'.

The main point is that Objects do not have numeric keys. If you try to specify one, it is converted to a string before it's stored. All Object keys are either Strings or Symbols.

When iterating an object, the system looks first for Strings that look like integer keys, and iterates those in numeric order, then iterates the remaining String keys, in insertion order, then iterates the Symbols, again in insertion order.

It's still a hot mess, but it's a different hot mess than you suggest.

Collapse
 
frehner profile image
Anthony Frehner

I believe this is actually covered in my article above, with an emphasis on this line:

Well, turns out that JS will see if your string can be converted to a number - if it can, then it will order it with the numbers and not the strings.

Collapse
 
cawoodm profile image
Marc

Ah... JavaScript the Bad Bits!

Collapse
 
iampraseo profile image
Praseo

Thank you for the explanation. :)
The title of the article should have been
"The evil ways of ordering properties in an object." :D

11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields

...

Read the whole post now!