Вводит ли 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
, выполните следующие действия для обычных объектов:
- Целочисленные индексы (если применимо) в порядке возрастания.
- Другие строковые ключи (если применимо) в порядке создания свойств.
- Символьные ключи (если применимо) в порядке создания свойств.
Это поведение определено во внутреннем методе [[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:
- Заказ элементы свойств так, что они находятся в том же относительном порядке, как будет подготовлена итератора, которые будут возвращены, если EnumerateObjectProperties внутренний метод был вызван с O.
Эти удаления означают, что начиная с EcmaScript 2020 и далее Object.keys
обеспечивает тот же конкретный порядок, что и Object.getOwnPropertyNames
а также Reflect.ownKeys
, а именно тот, который указан в OrdinaryOwnPropertyKeys. Порядок такой:
- Собственные свойства, являющиеся индексами массива,1 в порядке возрастания числового индекса
- Другие собственные свойства String в возрастающем хронологическом порядке создания свойства
- Собственные свойства символа в возрастающем хронологическом порядке создания свойства
1 индекс массива является строковым ключевым свойством, что каноническая цифровой строка2 которого числового значением я представляю собой целое число в диапазоне от +0 ≤ я <- 32 - 1.
2 канонических числовой строка является строковым представлением числа, которое будет подготовленоToString
, или строку "-0". Так, например, "012" не является канонической числовой строкой, а "12" - это.
Следует отметить, что все основные реализации уже были согласованы с этим порядком много лет назад.