Вводит ли 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: Нет

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

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

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

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

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

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

А также увидеть эту цитату из Object.keys:

Если реализация определяет конкретный порядок перечисления для оператора for-in, [...]

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

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

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

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

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


[1] Ниже написано:

[[Enumerate]] должен получить собственные ключи свойств целевого объекта, как если бы он вызывал его внутренний метод [[OwnPropertyKeys]].

И порядок [[OwnPropertyKeys]] четко определен. Но не позволяйте этому сбить вас с толку: это "как будто" означает только "те же свойства", а не "тот же порядок".

Это можно увидеть в EnumerableOwnNames, который использует [[OwnPropertyKeys]] для получения свойств, а затем упорядочивает их

в том же относительном порядке, который будет создан итератором, который будет возвращен, если был вызван внутренний метод [[Enumerate]]

Если бы [[Enumerate]] требовалось выполнять итерацию в том же порядке, что и [[OwnPropertyKeys]], не было бы необходимости изменять порядок.

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

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

Определение порядка перечисления for-in ( этап 4)

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

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

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

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

Порядок совпадает с порядком Reflect.ownKeys и другие методы, которые уже гарантированно повторяют этот способ.

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

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

Этот вопрос касается EcmaScript 2015 (ES6). Но следует отметить, что спецификация EcmaScript2017 удалила следующий абзац, который ранее появлялся в спецификации дляObject.keys, здесь цитируется из спецификации EcmaScript 2016:

Если реализация определяет конкретный порядок перечисления для оператора for-in, тот же порядок должен использоваться для элементов массива, возвращаемого на шаге 3.

Кроме того, спецификация EcmaScript 2020 удаляет следующий абзац из раздела, посвященногоEnumerableOwnPropertyNames, который все еще присутствует в спецификации EcmaScript 2019:

  1. Заказ элементы свойств так, что они находятся в том же относительном порядке, как будет подготовлена итератора, которые будут возвращены, если EnumerateObjectProperties внутренний метод был вызван с O.

Эти удаления означают, что начиная с EcmaScript 2020 и далее Object.keys обеспечивает тот же конкретный порядок, что и Object.getOwnPropertyNames а также Reflect.ownKeys, а именно тот, который указан в OrdinaryOwnPropertyKeys. Порядок такой:

  1. Собственные свойства, являющиеся индексами массива,1 в порядке возрастания числового индекса
  2. Другие собственные свойства String в возрастающем хронологическом порядке создания свойства
  3. Собственные свойства символа в возрастающем хронологическом порядке создания свойства

1 индекс массива является строковым ключевым свойством, что каноническая цифровой строка2 которого числового значением я представляю собой целое число в диапазоне от +0 ≤ я <- 32 - 1.

2 канонических числовой строка является строковым представлением числа, которое будет подготовленоToString, или строку "-0". Так, например, "012" не является канонической числовой строкой, а "12" - это.

Следует отметить, что все основные реализации уже были согласованы с этим порядком много лет назад.

Другие вопросы по тегам