Does Es6 Introduce a Well-Defined Order of Enumeration For Object Properties

Does ES6 introduce a well-defined order of enumeration for object properties?

Note: As of ES2020, even older operations like for-in and Object.keys are required to follow property order. That doesn't change the fact that using property order for fundamental program logic probably isn't a good idea, since the order for non-integer-index properties depends on when the properties were created.


Answer for ES2015-ES2019:

For for-in, Object.keys, and JSON.stringify: No.

For some other operations: Yes, usually.

While ES6 / ES2015 adds property order, it does not require for-in, Object.keys, or JSON.stringify to follow that order, due to legacy compatibility concerns.

for-in loops iterate according to [[Enumerate]], which is defined as (emphasis mine):

When the [[Enumerate]] internal method of O is called the following
steps are taken:

Return an Iterator object (25.1.1.2) whose next method iterates
over all the String-valued keys of enumerable properties of O. The
Iterator object must inherit from %IteratorPrototype% (25.1.2).
The mechanics and order of enumerating the properties is not
specified
but must conform to the rules specified below [1].

ES7 / ES2016 removes the [[Enumerate]] internal method and instead uses the abstract operation EnumerateObjectProperties, but just like [[Enumerate]] it doesn't specify any order.

And also see this quote from Object.keys:

If an implementation defines a specific order of enumeration for the
for-in statement, [...]

That means implementations are NOT required to define a specific order of enumeration. This has been confirmed by Allen Wirfs-Brock, Project Editor of the ECMAScript 2015 Language Specification, in a post made after the specification was complete.

Other operations, like Object.getOwnPropertyNames, Object.getOwnPropertySymbols, Object.defineProperties, and Reflect.ownKeys do follow the following order for ordinary objects:

  1. Integer indices (if applicable), in ascending order.
  2. Other string keys (if applicable), in property creation order.
  3. Symbol keys (if applicable), in property creation order.

This behavior is defined in the [[OwnPropertyKeys]] internal method. But certain exotic objects define that internal method slightly differently. For example, a Proxy's ownKeys trap may return an array in any order:

console.log(Reflect.ownKeys(new Proxy({}, {
ownKeys: () => ['3','1','2']
}))); // ['3','1','2'], the integer indices are not sorted!

Does JavaScript guarantee object property order?

The iteration order for objects follows a certain set of rules since ES2015, but it does not (always) follow the insertion order. Simply put, the iteration order is a combination of the insertion order for strings keys, and ascending order for number-like keys:

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }

Using an array or a Map object can be a better way to achieve this. Map shares some similarities with Object and guarantees the keys to be iterated in order of insertion, without exception:

The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion. (Note that in the ECMAScript 2015 spec objects do preserve creation order for string and Symbol keys, so traversal of an object with ie only string keys would yield keys in order of insertion)

As a note, properties order in objects weren’t guaranteed at all before ES2015. Definition of an Object from ECMAScript Third Edition (pdf):

4.3.3 Object

An object is a member of the
type Object. It is an unordered collection of properties each of which
contains a primitive value, object, or
function. A function stored in a
property of an object is called a
method.

JavaScript reorder object properties given an array of properties

Although ECMAScript-6 improved the feature, object properties are still notoriously unordered. A way to fix this would be to use a Map which remembers the original key insertion order.

    const obj = { a: 3, b: 10, c: 2, d: 7 };

const arr = ["c", "b", "a", "d"];

const res = new Map(arr.map(e => [e, obj[e]]));

for (let [key, value] of [...res]) {

  console.log(key, value);

}

Does JavaScript guarantee object property order?

The iteration order for objects follows a certain set of rules since ES2015, but it does not (always) follow the insertion order. Simply put, the iteration order is a combination of the insertion order for strings keys, and ascending order for number-like keys:

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }

Using an array or a Map object can be a better way to achieve this. Map shares some similarities with Object and guarantees the keys to be iterated in order of insertion, without exception:

The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion. (Note that in the ECMAScript 2015 spec objects do preserve creation order for string and Symbol keys, so traversal of an object with ie only string keys would yield keys in order of insertion)

As a note, properties order in objects weren’t guaranteed at all before ES2015. Definition of an Object from ECMAScript Third Edition (pdf):

4.3.3 Object

An object is a member of the
type Object. It is an unordered collection of properties each of which
contains a primitive value, object, or
function. A function stored in a
property of an object is called a
method.

How object works in javascript

Objects in JavaScript do not preserve the encounter order, to preserve the insertion order of keys use the new Map object:

function wordCount(str) {

tempStr = str.toUpperCase();

arr1 = tempStr.split(" ");

let frequencyConter1 = new Map();

for (let val of arr1 ){

frequencyConter1.set(val, ((frequencyConter1.get(val) || 0) + 1) );

}

for( let [key, value] of frequencyConter1){

console.log(`${key} ${value}`);

}

}

wordCount("My name is Xyz 1991 He is Abc Is he allright")


Related Topics



Leave a reply



Submit