Включает ли ES6 четко определенный порядок перечисления свойств объекта?

Предоставляет ли ES6 четко определенный порядок перечисления свойств объекта?

var o = {
  '1': 1,
  'a': 2,
  'b': 3
}

Object.keys(o); // ["1", "a", "b"] - is this ordering guaranteed by ES6?

for(let k in o) {
  console.log(k);
} // 1 2 3 - is this ordering guaranteed by ES6?

Ответ 1

Для for-in, Object.keys и JSON.stringify: Нет.

Для некоторых других операций: Да, обычно.

Хотя ES6/ES2015 добавляет порядок свойств, для него не требуется, чтобы for-in, Object.keys или JSON.stringify следовали этому порядку из-за устаревших проблем совместимости.

for-in цикл повторяется в соответствии с [[Enumerate]], который [определяется как (выделено мной):

Когда внутренний метод [[Enumerate]] для O вызывается следующим шаги предприняты:

Вернуть объект Iterator (25.1.1.2), следующий метод которого повторяется над всеми строковыми ключами перечислимых свойств О. Объект Iterator должен наследоваться от% IteratorPrototype% (25.1.2). Механика и порядок перечисления свойств не указано, но должно соответствовать правилам, указанным ниже [1].

ES7/ES2016 удаляет внутренний метод [[Enumerate]] и вместо этого использует абстрактную операцию EnumerateObjectProperties, но точно так же, как [[Enumerate]], не задает никакой порядок.

А также посмотрите цитату из Object.keys:

Если реализация определяет конкретный порядок перечисления для выражение о вступлении в силу, [...]

Это означает, что реализации НЕ обязаны определять конкретный порядок перечисления. Это было подтверждено Алленом Вирфсом-Броком, редактором проекта спецификации языка ECMAScript 2015, в сообщении, сделанном после завершения спецификации.

Другие операции, такие как Object.getOwnPropertyNames, Object.getOwnPropertySymbols, Object.defineProperties и Reflect.ownKeys, выполняются в следующем порядке для обычные объекты:

  1. Целочисленные индексы (если применимо) в порядке возрастания.
  2. Другие строковые ключи (если применимо) в порядке создания свойств.
  3. Символьные ключи (если применимо) в порядке создания свойств.

Это поведение определено во внутреннем методе [[OwnPropertyKeys]]. Но некоторые экзотические объекты определяют этот внутренний метод немного по-другому. Например, ловушка Proxy ownKeys может возвращать массив в любом порядке:

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

Ответ 2

Как описано в другом ответе, ES2015 не определяет порядок перечисления для (очень часто используемых) итерационных методов свойств for-in, Object.keys и JSON.stringify, тогда как он определяет методы перечисления для других методов, таких как Reflect.ownKeys. Однако это (вероятно, скоро) больше не будет иметь место.

Как многие, вероятно, наблюдали в своем опыте работы с JS и в комментариях, хотя порядок итераций свойств не гарантируется спецификацией для этих методов, каждая реализация почти всегда выполняет итерацию в одном и том же детерминированном порядке. В результате есть предложение изменить спецификацию, чтобы сделать это поведение официальным:

Указание порядка перечисления for-in (Этап 3)

С этим предложением, в большинстве случаев, for..in, Object.keys/values/entries и JSON.stringify гарантированно будут повторяться в следующем порядке:

(1) Ключи числового массива

(2) не символьные ключи в порядке вставки

(3) символьные клавиши в порядке вставки

Это тот же порядок, что и для Reflect.ownKeys и других методов, которые уже гарантированно повторяют этот путь.

текст спецификации довольно прост: EnumerateObjectProperties, проблемный абстрактный метод, вызванный for..in и т.д., Порядок которого раньше не указывался, теперь будет вызывать [[OwnPropertyKeys]], который является внутренним методом, для которого указан порядок итераций.

Есть несколько странных случаев, с которыми реализации в настоящее время не согласуются, и в таких случаях результирующий порядок будет по-прежнему не определен, но таких случаев немного и они далеко друг от друга.