4

There's a number of posts here about this issue, and they all contain a lot of assertions that can be summarized like this:

  1. Object properties are never guaranteed to be ordered in any way.
  2. JSON.parse() never sorts properties in any way.

Obviously we tend to have no doubt about #1 above, so we may reasonably expect that, for any operation, properties are processed merely in the order they appear.
[edit, following the @Bergi's comment: or at least they should appear in a random order]

Then from that we might especially infer that #2 should be true.

But look at this snippet:
(BTW note: to show the results, snippets below don't use console.log() which may itself change order of the output. Instead objects are iterated by for (key in obj) and the output displayed in the document)

var inputs = [
  '{"c": "C", "a": "A", "b": "B"}',
  '{"3": "C", "1": "A", "2": "B"}',
  '{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}'
];

for (var i in inputs) {
  var json = inputs[i],
      parsed = JSON.parse(json),
      output = [];
  for (var j in parsed) {
    output.push(j + ': ' + parsed[j]);
  }
  document.write(`JSON: ${json}<br />Parsed: ${output.join(', ')})<hr />`);
}

It shows that, given a JSON string having unordered keys:

  • When the input has keys with non-numeric values, the parsed object has its properties in the same order than in the input. This is consistent with the #2 assumption above.
  • Conversely when the input has keys with numeric values (though they're strings, so not firing parse error), the parsed object has its properties sorted. This now contradicts the #2 assumption.
  • More: when there are mixed numeric and non-numeric key values, first appear the numeric properties sorted, then the non-numeric properties in their original order.

From that I was first tempted to conclude that actually there would be a (non-documented?) feature, so JSON.parse() works following the "rules" exposed above.

But I had the idea to look further, so the snippet below now shows how ordered are the properties of a merely coded object:

var objects = [
  [
    '{"c": "C", "a": "A", "b": "B"}',
    {"c": "C", "a": "A", "b": "B"}
  ],
  [
    '{"3": "C", "1": "A", "2": "B"}',
    {"3": "C", "1": "A", "2": "B"}
  ],
  [
    '{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}',
    {"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}
  ]
];

for (var i in objects) {
  var object = objects[i],
      output = [];
  for (var j in object[1]) {
    output.push(j + ': ' + object[1][j]);
  }
  document.write(`Code: ${object[0]}<br />Object: ${output.join(', ')}<hr />`);
}

It results in analogue observations, i.e. whichever order they're coded, properties are stored following the 3rd rule above:

  • numerically named properties are all put first, sorted
  • other properties are set next, ordered as coded

So it means that JSON.parse() is not involved: in fact it seems to be a fundamental process of object building.
Again this appears not documented, at least as far I could find.

Any clue for a real, authoritative, rule?


[Edit, thanks to @Oriol's answer] It actually appears that, synthetically:

  • This behaviour conforms to an ECMA specification rule.
  • This rule should apply to all methods where a specific order is guaranteed but is optional for other cases.
  • However it seems that modern browsers all choose to apply the rule whatever method is involved, hence the apparent contradiction.
cFreed
  • 4,404
  • 1
  • 23
  • 33
  • 1
    **Not for for..in**. Dupe of [Does ES6 introduce a well-defined order of enumeration for object properties?](http://stackoverflow.com/q/30076219/1529630)? – Oriol Oct 11 '16 at 19:23
  • @Oriol I totally agree. But it's just where's the point: assumed that "legally" no peculiar order is followed, we observe that however a certain ordering rule is actually used. Here is what puzzles me. – cFreed Oct 11 '16 at 19:32

2 Answers2

3

The properties of an object have no order, so JSON.parse can't sort them. However, when you list or enumerate the properties of an object, the order may be well-defined or not.

Not necessarily for for...in loops nor Object.keys

As fully explained in Does ES6 introduce a well-defined order of enumeration for object properties?, the spec says

The mechanics and order of enumerating the properties is not specified

But yes for OrdinaryOwnPropertyKeys

Objects have an internal [[OwnPropertyKeys]] method, which is used for example by Object.getOwnPropertyNames and Object.getOwnPropertySymbols.

In the case of ordinary objects, that method uses the OrdinaryGetOwnProperty abstract operation, which returns properties in a well-defined order:

When the abstract operation OrdinaryOwnPropertyKeys is called with Object O, the following steps are taken:

  1. Let keys be a new empty List.
  2. For each own property key P of O that is an integer index, in ascending numeric index order
    1. Add P as the last element of keys.
  3. For each own property key P of O that is a String but is not an integer index, in ascending chronological order of property creation
    1. Add P as the last element of keys.
  4. For each own property key P of O that is a Symbol, in ascending chronological order of property creation
    1. Add P as the last element of keys.
  5. Return keys.

Therefore, since an order is required by OrdinaryOwnPropertyKeys, implementations may decide to internally store the properties in that order, and use it too when enumerating. That's what you observed, but you can't rely on it.

Also be aware non-ordinary objects (e.g. proxy objects) may have another [[OwnPropertyKeys]] internal method, so even when using Object.getOwnPropertyNames the order could still be different.

Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • I'm a bit embarrassed. On a side your answer here adds an information which precisely explains the behaviour I pointed in my question. But in the other side I keep puzzled, just because you confirm that `for...in` should not (necessarily) use a peculiar order, while at the opposite I get it in my tests. Also because, apart from this "deep level" ECMA reference, I didn't find this information in ordinary documentation like MDN. May be my search was wrong? Anyway, thanks for this informative answer. – cFreed Oct 11 '16 at 20:00
  • @cFreed The specification does not define any order for `for...in`. You are testing on a implementation which seems to use the same order as OrdinaryOwnPropertyKeys. Another implementation might not. Maybe there is not much documentation because before ES6 the spec didn't require any order for `Object.getOwnPropertyNames` neither. But MDN is a wiki, you can update it. – Oriol Oct 11 '16 at 20:05
0

so we may reasonably expect that, for any operation, properties are processed merely in the order they appear

That's where the flaw in the reasoning lies. Given that object properties aren't guaranteed to be ordered, we have to assume that any operation processes properties in any order that it sees fit.

And in fact engines evolved in a way that treat integer properties specially - they're like array indices, and are stored in a more efficient format than a lookup table.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I agree that "expect...properties are...merely in the order they appear" may be naive. But at least they should appear randomly. It's why my question remains: why, at the opposite, is it quite precisely oredered? Please also see my comment below the Oriol's answer. – cFreed Oct 11 '16 at 20:04
  • Because the engines chose an efficient implementation, not a random one. And in fact if you tested with different engines (i.e. browsers, particularly older ones), you'd get different results. It's just that they converged to the approach that treats integer indices in ascending order and everything else in order of appearance (which eventually got specced for some operations in ES6). – Bergi Oct 11 '16 at 20:09