Object properties do have a certain order, differing from insertion order. It's described in the ECMAScript 2015 Language Specification:
9.1.12 [[OwnPropertyKeys]] ( )
When the [[OwnPropertyKeys]] internal method of O is called the following steps are taken:
- Let keys be a new empty List.
- For each own property key P of O that is an integer index, in ascending numeric index order
a. Add P as the last element of keys.
- For each own property key P of O that is a String but is not an integer index, in property creation order
a. Add P as the last element of keys.
- For each own property key P of O that is a Symbol, in property creation order
a. Add P as the last element of keys.
- Return keys.
In the above, the properties are defined as the List keys. Then, when a property that is an integer is encountered, it is added to List keys in ascending numeric order, thus leading to 3, 5, 8 you observe.
If you want ordered keys and value pairs that respects order based on insertion, you must use something that respects order, such as an ES6 Map. Per the documentation:
A Map object iterates its elements in insertion order — a for...of loop returns an array of [key, value] for each iteration.
Here's an example:
const obj = new Map([
[5, "test 1"],
[3, "test 2"],
[8, "test 3"]
]);
for(let [key, value] of obj) {
console.log(key, value);
}
First, you pass an array of arrays to the constructor. The Map constructor takes in an iterable, and uses the subarrays as key and value pairs. The Map essentially looks like this:
+-----+----------+
| Key | Value |
+-----+----------+
| 5 | "test 1" |
| 3 | "test 2" |
| 8 | "test 3" |
+-----+----------+
Next you use a for...of loop to iterate over the Map. As noted in the documentation, an array of [key, value] is returned by the iterator so you can use array destructuring. Thus, key and value hold the values of the keys and values of the Map respectively, and logging them provides the desired, ordered result.