Чи гарантує JavaScript об'єкт власності на замовлення?


647

Якщо я створю такий об’єкт:

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";

Чи завжди такий предмет буде виглядати таким чином?

{ prop1 : "Foo", prop2 : "Bar" }

Тобто, чи будуть властивості в тому ж порядку, як я їх додав?





1
@TJCrowder Не могли б ви детальніше зупинитися на тому, чому прийнята відповідь вже не точна? Зв'язане питання, схоже, зводиться до думки, що замовлення власності все ще не гарантується за специфікацією.
нуль298

2
@ zero298: прийнята відповідь на це запитання чітко описує вказаний порядок властивості станом на ES2015 +. Спадкові операції ( for-in, Object.keys) не повинні підтримувати це (офіційно), але зараз є порядок. (Неофіційно: Firefox, Chrome та Edge всі дотримуються зазначеного порядку навіть у фор-і та Object.keys, де їх офіційно не потрібно: jsfiddle.net/arhbn3k2/1 )
TJ Crowder

Відповіді:


474

Порядок ітерації об’єктів дотримується певного набору правил, починаючи з ES2015, але він (не завжди) дотримується порядку вставки . Простіше кажучи, порядок ітерації - це поєднання порядку вставки для рядкових клавіш і порядку зростання для числових клавіш:

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

Використання масиву або Mapоб'єкта може бути кращим способом досягти цього. Mapділиться деякими подібностями Objectта гарантує безперервне повторення ключів у порядку вставки :

Ключі в Map впорядковуються, поки ключі, додані до об'єкта, не є. Таким чином, під час ітерації над ним об’єкт Map повертає ключі в порядку вставки. (Зверніть увагу, що в специфікаціях ECMAScript 2015 зберігаються порядок створення для рядкових та символьних клавіш, тому обхід об'єкта, тобто лише рядкові клавіші, дасть ключі в порядку вставки)

Як зауваження, порядок властивостей в об’єктах взагалі не гарантувався до ES2015. Визначення об’єкта з третьої версії ECMAScript (pdf) :

4.3.3 Об'єкт

Об'єкт є членом типу Object. Це невпорядкована колекція властивостей, кожна з яких містить примітивне значення, об'єкт чи функцію. Функція, що зберігається у властивості об'єкта, називається методом.


Поведінка цілих клавіш не однакова у всіх браузерах. Деякі старі браузери повторюють цілі клавіші у порядку вставки (за допомогою рядкових клавіш), а деякі - у порядку зростання.
Дейв Допсон

1
@DaveDopson - Право - застарілі браузери не відповідають поточній специфікації, оскільки вони не оновлюються.
TJ Crowder

199

ТАК (для не цілих ключів).

Більшість браузерів ітератують властивості об’єкта як:

  1. Цілі клавіші у порядку зростання (і рядки типу "1", які розбираються як ints)
  2. Строкові клавіші в порядку вставки (ES2015 гарантує це і всі браузери відповідають)
  3. Імена символів у порядку вставки (ES2015 гарантує це та всі браузери відповідають)

Деякі старі браузери поєднують категорії №1 та №2, повторюючи всі клавіші в порядку вставки. Якщо ваші ключі можуть розібратися як цілі числа, краще не покладатися на якийсь конкретний порядок ітерації.

Порядок вставки поточної мови (починаючи з ES2015) зберігається, за винятком випадків, коли ключі розбираються на цілі числа (наприклад, "7" або "99"), де поведінка змінюється між браузерами. Наприклад, Chrome / V8 не дотримується порядку вставки, коли клавіші розбираються як числові.

Спеціалізація старої мови (до ES2015) : Порядок ітерацій технічно не визначений, але всі основні браузери відповідали поведінці ES2015.

Зауважте, що поведінка ES2015 була хорошим прикладом того, що мовна специфіка керується наявною поведінкою, а не навпаки. Щоб отримати більш глибоке розуміння цього способу зворотної сумісності, див. Http://code.google.com/p/v8/isissue/detail?id=164 , помилка Chrome, яка детально висвітлює дизайнерські рішення, що стоять за поведінкою порядку ітерації Chrome . За одним із (досить висловлюваних) коментарів до цього звіту про помилку:

Стандарти завжди слідують за реалізаціями, саме звідси походить XHR, і Google робить те саме, реалізуючи Gears і використовуючи аналогічні функції HTML5. Правильне виправлення полягає в тому, щоб ECMA офіційно включив стандартну поведінку фактично в наступний ревізор специфікації.


2
@BenjaminGruenbaum - саме це було моєю точкою. Станом на 2014 рік усі основні виробники мали спільну реалізацію, і таким чином стандарти згодом будуть дотримуватися (тобто у 2015 році).
Дейв Допсон

2
До речі: API ReactcreateFragment вже покладається на це ... 🤔
mik01aj

7
@BenjaminGruenbaum Ваш коментар хибний. У ES2015 замовлення гарантується лише для обраних методів. Див відповідь на Ftor нижче.
Пьотр Доброгост

82

Порядок власності в звичайних об'єктах - це складний предмет у Javascript.

Якщо в ES5 явно не вказано жодне замовлення, ES2015 має замовлення в певних випадках. Дано наступний об'єкт:

o = Object.create(null, {
  m: {value: function() {}, enumerable: true},
  "2": {value: "2", enumerable: true},
  "b": {value: "b", enumerable: true},
  0: {value: 0, enumerable: true},
  [Symbol()]: {value: "sym", enumerable: true},
  "1": {value: "1", enumerable: true},
  "a": {value: "a", enumerable: true},
});

Це призводить до наступного порядку (у певних випадках):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. цілочисельні клавіші у порядку зростання
  2. звичайні клавіші в порядку вставки
  3. Символи в порядку вставки

Таким чином, є три сегменти, які можуть змінювати порядок вставки (як це було в прикладі). І цілі клавіші зовсім не дотримуються порядку вставки.

Питання полягає в тому, якими методами гарантується цей порядок у специфікації ES2015?

Наступні методи гарантують показаний порядок:

  • Object.assign
  • Об'єкт.defineProperties
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys

Наступні методи / петлі гарантують взагалі не замовлення:

  • Object.keys
  • за..ін
  • JSON.parse
  • JSON.stringify

Висновок: Навіть у ES2015 ви не повинні покладатися на порядок властивостей звичайних об'єктів у Javascript. Він схильний до помилок. Використовуйте Mapзамість цього.


Я грубо перевірив висновок у вузлі v8.11, і він правильний.
merlin.ye

1
@BenjaminGruenbaum якісь думки?
evolutionxbox

Гарантійне замовлення на Object.entries, керуючись правилом цілого числа,
Мохімі

1
+1 для "Навіть у ES2015 ви не повинні покладатися на порядок властивостей звичайних об'єктів у Javascript. Він схильний до помилок. Замість цього використовуйте Map." Ви не можете розраховувати на замовлення власності, коли дотримання вимог суперечить. Як ви навіть перевіряєте це на наявність порівнянності, якщо вам потрібно підтримувати старі браузери?
нуль298

1
Чи є офіційна документація про це чи довідка?
Удар

66

На момент написання запиту більшість браузерів повертало властивості в тому ж порядку, що і вони були вставлені, але це явно не гарантувало поведінку, тому на неї не слід покладатися.

Специфікація ECMAScript використовується для того, щоб сказати:

Механіка та порядок перерахування властивостей ... не вказані.

Однак у ES2015 та пізніших нецілих ключах буде повернуто в порядку вставки.


16
Chrome реалізує інше замовлення для інших браузерів. Дивіться code.google.com/p/v8/isissue/detail?id=164
Tim Down

9
Opera 10.50 і вище, а також IE9 відповідають порядку Chrome. Firefox і Safari зараз меншість (і обидва вони також використовують різні замовлення для об'єктів / масивів).
gsnedders

1
@Veverke явно немає гарантії на замовлення, тому завжди слід вважати, що замовлення фактично випадкове.
Альнітак

1
@Veverke Ні, порядок не такий, як передбачуваний. Це залежить від реалізації та може змінюватися в будь-який час, і може змінюватися (наприклад) щоразу, коли ваш веб-переглядач оновлюється.
Альнітак

3
Ця відповідь є неправдивою у ES2015.
Бенджамін Груенбаум

42

Вся ця відповідь полягає в контексті відповідності специфіці, а не тому, що робить будь-який двигун в конкретний момент чи історично.

Взагалі ні

Актуальне питання дуже розпливчасте.

чи будуть властивості в тому ж порядку, як я їх додав

У якому контексті?

Відповідь: це залежить від ряду факторів. Загалом, ні .

Іноді так

Тут ви можете розраховувати на замовлення ключа власності для звичайного Objects:

  • Двигун, сумісний з ES2015
  • Власні властивості
  • Object.getOwnPropertyNames(), Reflect.ownKeys(),Object.getOwnPropertySymbols(O)

У всіх випадках ці методи включають в себе безлічі ключі властивостей та ключі замовлення, як зазначено у [[OwnPropertyKeys]](див. Нижче). Вони відрізняються за типом ключових значень, які вони включають ( Stringта / або Symbol). У цьому контексті Stringвключаються цілі значення.

Object.getOwnPropertyNames(O)

Повертає властивості Oвласного Stringзображення ( назви властивостей ).

Reflect.ownKeys(O)

Повертає властивості Oвласного Stringта Symbolключового зображення.

Object.getOwnPropertySymbols(O)

Повертає властивості Oвласного Symbolзображення.

[[OwnPropertyKeys]]

Порядок по суті: цілочисельний Stringsу порядку зростання, не цілий, як Stringsу порядку створення, символи в порядку створення. Залежно від того, яка функція викликає це, деякі з цих типів можуть не включатися.

Конкретна мова полягає в тому, що ключі повертаються в такому порядку:

  1. ... кожен власний ключ Pвластивості O[ітераційний об'єкт], який є цілим індексом, у порядку зростання числового індексу

  2. ... кожен власний ключ Pвластивості Oцього рядка, але не є цілим індексом, у порядку створення властивостей

  3. ... кожен власний ключ властивості Pв Oтому , що це символ, для того створення майна

Map

Якщо вас цікавлять впорядковані карти, вам варто скористатися Mapтипом, введеним в ES2015, а не простим Objects.



7

У ES2015 це робиться, але не до того, що ви можете подумати

Порядок ключів в об’єкті не гарантувався до ES2015. Це було визначено реалізацією.

Однак у ES2015 в Росії було вказано. Як і багато речей у JavaScript, це було зроблено для сумісності та, як правило, відображало існуючий неофіційний стандарт серед більшості двигунів JS (виняток - хто виняток).

Порядок визначений у специфікації, під абстрактною операцією OrdinaryOwnPropertyKeys , яка лежить в основі всіх методів ітерації над власними ключами об'єкта. Перефразоване, порядок такий:

  1. Все ціле число індексних ключів ( такі речі , як "1123", "55"і т.д.) в висхідному числовому порядку.

  2. Усі рядкові клавіші, які не є цілими індексами, в порядку створення (найстаріший-перший).

  3. Усі клавіші символів, у порядку створення (найстаріший-перший).

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

Деякі винятки включають методи перерахування успадкованих ключів, наприклад for .. inцикл. for .. inЦикл не гарантує порядок у відповідності зі специфікацією.


7

Станом на ES2015, замовлення власності гарантується для певних методів, які повторюють властивості. але не інші . На жаль, як правило, найчастіше застосовуються методи, на які не гарантується замовлення:

  • Object.keys, Object.values,Object.entries
  • for..in петлі
  • JSON.stringify

Але, як і в ES2020, замовлення власності для цих раніше недовірливих методів буде гарантоване специфікацією, яку слід повторити таким же детермінованим чином, як і інші, завдяки готовій пропозиції: для механіки .

Як і у випадку з методами, які мають гарантований порядок ітерації (як Reflect.ownKeysі Object.getOwnPropertyNames), раніше не визначені методи також повторять у наступному порядку:

  • Цифрові клавіші масиву у порядку числення у порядку зростання
  • Усі інші клавіші без символів у порядку вставки
  • Клавіші символів у порядку вставки

Це те, що майже кожна реалізація вже робить (і робиться протягом багатьох років), але нова пропозиція зробила це офіційним.

Незважаючи на те, що поточна специфікація залишає для ... в порядку ітерації " майже повністю не визначений , реальні двигуни мають тенденцію бути більш послідовними:"

Відсутність специфіки в ECMA-262 не відображає реальність. Обговорюючи роки, виконавці зауважили, що існують певні обмеження в поведінці for-in, за якими повинен дотримуватися кожен, хто хоче запустити код у мережі.

Оскільки кожна реалізація вже повторює властивості передбачувано, її можна помістити в специфікацію, не порушуючи зворотної сумісності.


Є кілька дивних випадків, щодо яких реалізація на даний момент не погоджується, і в таких випадках отримане замовлення буде надалі не визначено. Щоб замовлення майна було гарантоване :

Ні об'єкт, який повторюється, ні що-небудь в ланцюзі прототипу не є проксі-сервером, набраним масивом, об'єктом простору імен модулів або хостовим екзотичним об'єктом.

Ні об’єкт, ні що-небудь у ланцюзі прототипу не змінювалося для прототипу під час ітерації.

Ні об’єкт, ні щось у ланцюзі прототипу не має властивості, видаленої під час ітерації.

Ніщо в прототипі ланцюга об'єкта не має властивості, доданої під час ітерації.

Жодна властивість об'єкта чи нічого в ланцюзі прототипу не змінюється його перелічуваність під час ітерації.

Жодне нечислиме майно не затінює перелічене.


5

Як заявили інші, у вас немає гарантії порядку, коли ви повторюєте властивості об'єкта. Якщо вам потрібен упорядкований список декількох полів, я запропонував створити масив об’єктів.

var myarr = [{somfield1: 'x', somefield2: 'y'},
{somfield1: 'a', somefield2: 'b'},
{somfield1: 'i', somefield2: 'j'}];

Таким чином ви можете використовувати звичайний цикл і мати порядок вставки. Потім ви можете використовувати метод сортування Array для сортування цього в новому масиві, якщо це необхідно.


3

Щойно це з’ясувалося важким шляхом.

Використовуючи React with Redux, контейнер стану ключів, з якими я хочу перейти, щоб генерувати дітей, оновлюється щоразу, коли магазин змінюється (відповідно до концепцій про незмінність Redux).

Таким чином, для того, щоб взяти, Object.keys(valueFromStore)я використовував Object.keys(valueFromStore).sort(), щоб я принаймні зараз мав алфавітний порядок для ключів.


-7

Зі стандарту JSON :

Об'єкт - це не упорядкована колекція нульових або більше пар імен / значень, де ім'я - це рядок, а значення - рядок, число, булеве значення, null, об'єкт або масив.

(наголос мій).

Отже, ні, ви не можете гарантувати замовлення.


7
це визначено стандартом ECMAScript - не специфікацією JSON.
Альнітак

7
@Alnitak, @Iacqui: JSON бере це лише із специфікації ECMAScript. Він також вказаний для JSON, але це не стосується питання.
Paŭlo Ebermann
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.