Гарантирует ли JavaScript порядок свойств объекта?
Если я создаю объект, как это:
var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
Будет ли результирующий объект всегда выглядеть так?
{ prop1 : "Foo", prop2 : "Bar" }
То есть свойства будут в том же порядке, в котором я их добавил?
14 ответов
Нет, порядок свойств в объектах не гарантируется в JavaScript; вам нужно использовать Array
,
Определение объекта из ECMAScript Third Edition (pdf):
4.3.3 Объект
Объект является членом типа Object. Это неупорядоченный набор свойств, каждое из которых содержит примитивное значение, объект или функцию. Функция, хранящаяся в свойстве объекта, называется методом.
Начиная с ECMAScript 2015, используя Map
объект может быть альтернативой. Map
разделяет некоторые сходства с Object
и гарантирует порядок ключей:
Карта итерирует свои элементы в порядке вставки, тогда как порядок итераций не указан для объектов.
Текущий язык Спецификация: технически, порядок не указан.
Текущие браузеры: порядок сохраняется с основным исключением ключей, таких как "7", которые обрабатываются как целые числа и обрабатываются по-разному в Chrome / V8.
Future Language Spec (> ES2015): Как правило, можно ожидать, что вещи, заказанные сегодня, не станут неупорядоченными. Новые API гарантируют порядок; существующие API трудно изменить. Смотрите ответ JMM для более подробной информации.
Лучшая ссылка ниже в комментарии Тима Дауна:
http://code.google.com/p/v8/issues/detail?id=164
Эта ошибка подробно описывает конструктивные решения, связанные с реализацией порядка расположения ключей в Chrome. Один из выводов заключается в том, что для строковых ключей, которые не анализируются с целым числом (то есть "a" или "b", но НЕ "3"), ключи печатаются в порядке вставки во всех основных браузерах, и хотя это поведение не "стандартизировано", оно считается серьезной проблемой обратной совместимости от поставщиков браузеров. Используйте на свой риск.
За один из (довольно самоуверенных) комментариев:
Стандарты всегда следуют за реализациями, отсюда и XHR, и Google делает то же самое, внедряя Gears, а затем применяя эквивалентную функциональность HTML5. Правильное решение заключается в том, чтобы ECMA формально включила стандартное поведение де-факто в следующую версию спецификации.
Если вы полагаетесь на порядок вставки, вы находитесь за пределами спецификации ECMAScript, но в рамках фактического стандарта обычного поведения браузера, если ваши ключи не анализируются как целые числа.
Порядок свойств в обычных объектах является сложным предметом в 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"
}
- целочисленные ключи в порядке возрастания
- нормальные ключи в порядке вставки
- Символы в порядке вставки
Таким образом, есть три сегмента, которые могут изменить порядок вставки (как это было в примере). И целочисленные ключи вообще не придерживаются порядка вставки.
Вопрос в том, для каких методов этот заказ гарантирован в спецификации ES2015?
Следующие методы гарантируют указанный порядок:
- Object.assign
- Object.defineProperties
- Object.getOwnPropertyNames
- Object.getOwnPropertySymbols
- Reflect.ownKeys
Следующие методы / циклы гарантируют отсутствие порядка вообще:
- Object.keys
- for..in
- JSON.parse
- JSON.stringify
Вывод: даже в ES2015 вы не должны полагаться на порядок свойств обычных объектов в Javascript. Это склонно к ошибкам. использование Map
вместо.
На момент написания большинство браузеров возвращали свойства в том же порядке, в котором они были вставлены, но это явно не гарантированное поведение, поэтому на него не следует полагаться.
Спецификация ECMAScript используется, чтобы сказать:
Механика и порядок перечисления свойств... не уточняется.
Однако в ES2015 и более поздних нецелочисленные ключи будут возвращены в порядке вставки.
Весь этот ответ находится в контексте соответствия спецификациям, а не тому, что любой двигатель делает в определенный момент или исторически.
Как правило, нет
Фактический вопрос очень расплывчатый.
свойства будут в том же порядке, в котором я их добавил
В каком контексте?
Ответ: это зависит от ряда факторов. В общем нет.
Иногда да
Здесь вы можете рассчитывать на порядок ключа свойства для простого 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
в порядке создания, символы в порядке создания. В зависимости от того, какая функция вызывает это, некоторые из этих типов могут быть не включены.
Определенным языком является то, что ключи возвращаются в следующем порядке:
... каждый собственный ключ свойства
P
изO
[объект повторяется], который является целочисленным индексом в порядке возрастания числового индекса... каждый собственный ключ свойства
P
изO
это строка, но не целочисленный индекс, в порядке создания свойств... каждый собственный ключ свойства
P
изO
это символ, в порядке создания свойства
Map
Если вы заинтересованы в заказанных картах, вы должны рассмотреть возможность использования Map
тип введен в ES2015 вместо простого Objects
,
Начиная с ES2015, порядок свойств гарантирован для определенных методов, которые перебирают свойства. но не другие. К сожалению, чаще всего используются методы, для которых не гарантируется наличие порядка:
Object.keys
,Object.values
,Object.entries
for..in
петлиJSON.stringify
Но, начиная с ES2020, порядок свойств для этих ранее ненадежных методов будет гарантирован спецификацией и будет повторяться таким же детерминированным образом, как и другие, из-за законченного предложения: механика for-in.
Как и в случае с методами, имеющими гарантированный порядок итераций (например, Reflect.ownKeys
а также Object.getOwnPropertyNames
), ранее не указанные методы также будут повторяться в следующем порядке:
- Ключи числового массива в возрастающем числовом порядке
- Все остальные ключи, не являющиеся символами, в порядке вставки
- Символьные клавиши в порядке вставки
Это то, что уже делают практически все реализации (и делали это уже много лет), но новое предложение сделало это официальным.
Хотя текущая спецификация оставляет... в порядке итераций " почти полностью неопределенным, реальные движки, как правило, более согласованы":
Отсутствие специфичности в ECMA-262 не отражает реальности. В ходе обсуждения, проведенного несколько лет назад, разработчики отметили, что существуют некоторые ограничения на поведение for-in, которым должен следовать любой, кто хочет запускать код в сети.
Поскольку каждая реализация уже предсказуемо выполняет итерацию по свойствам, ее можно включить в спецификацию без нарушения обратной совместимости.
Есть несколько странных случаев, в которых реализации в настоящее время не согласны, и в таких случаях результирующий порядок останется неопределенным. Для гарантии порядка собственности:
Ни повторяемый объект, ни что-либо в его цепочке прототипов не является прокси, типизированным массивом, объектом пространства имен модуля или экзотическим объектом хоста.
Ни объект, ни что-либо в его цепочке прототипов не меняются во время итерации.
Ни объект, ни что-либо в его цепочке прототипов не имеют свойства, удаленного во время итерации.
Ничто в цепочке прототипов объекта не имеет свойства, добавленного во время итерации.
Ни одно свойство объекта или что-либо в его цепочке прототипов не изменяется во время итерации.
Никакое неперечислимое свойство не затмевает перечислимое.
В современных браузерах вы можете использовать Map
структура данных вместо объекта.
Объект Map может перебирать свои элементы в порядке вставки...
В ES2015 так и есть, но не то, что вы думаете
Порядок ключей в объекте не гарантировался до ES2015. Это было определено реализацией.
Тем не менее, в ES2015 в был указан. Как и многие другие вещи в JavaScript, это было сделано в целях совместимости и, как правило, отражало существующий неофициальный стандарт среди большинства движков JS (за исключением, вы знаете, кто).
Порядок определяется в спецификации в рамках абстрактной операции OrdinaryOwnPropertyKeys, которая лежит в основе всех методов итерации по собственным ключам объекта. Перефразируя, порядок выглядит следующим образом:
Все целочисленные индексные ключи (например,
"1123"
,"55"
и т. д.) в порядке возрастания номеров.Все строковые ключи, которые не являются целочисленными индексами, в порядке их создания (самый старый-первый).
Все символьные клавиши в порядке создания (самые старые-первые).
Глупо говорить, что порядок ненадежен - он надежен, просто, вероятно, это не то, что вам нужно, и современные браузеры правильно его реализуют.
Некоторые исключения включают методы перечисления унаследованных ключей, такие как for .. in
петля. for .. in
Цикл не гарантирует порядок согласно спецификации.
Как уже говорили другие, у вас нет никаких гарантий относительно порядка, когда вы перебираете свойства объекта. Если вам нужен упорядоченный список из нескольких полей, я предложил создать массив объектов.
var myarr = [{somfield1: 'x', somefield2: 'y'},
{somfield1: 'a', somefield2: 'b'},
{somfield1: 'i', somefield2: 'j'}];
Таким образом, вы можете использовать обычный цикл for и иметь порядок вставки. Затем вы можете использовать метод сортировки Array, чтобы отсортировать его в новый массив, если это необходимо.
Основное различие между объектом и картой на примере:
это Порядок итерации в цикле, в Map он следует порядку, установленному при создании, тогда как в OBJECT - нет.
СМОТРЕТЬ:ОБЪЕКТ
var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
obj['1'] = "day";
console.log(obj)
**OUTPUT: {1: "day", prop1: "Foo", prop2: "Bar"}**
КАРТА
let myMap = new Map()
// setting the values
myMap.set("foo", "value associated with 'a string'")
myMap.set("Bar", 'value associated with keyObj')
myMap.set("1", 'value associated with keyFunc')
OUTPUT:
**1. ▶0: Array[2]
1. 0: "foo"
2. 1: "value associated with 'a string'"
2. ▶1: Array[2]
1. 0: "Bar"
2. 1: "value associated with keyObj"
3. ▶2: Array[2]
1. 0: "1"
2. 1: "value associated with keyFunc"**
Просто выяснил это трудным путем.
Используя React с Redux, контейнер состояний, ключи которого я хочу просмотреть для генерации потомков, обновляется каждый раз, когда изменяется хранилище (согласно концепции неизменяемости Redux).
Таким образом, чтобы принять Object.keys(valueFromStore)
я использовал Object.keys(valueFromStore).sort()
, так что у меня по крайней мере теперь есть алфавитный порядок для клавиш.
Для 100% отказоустойчивого решения вы можете использовать вложенные объекты и сделать что-то вроде этого:
const obj = {};
obj.prop1 = {content: "Foo", index: 0};
obj.prop2 = {content: "Bar", index: 1};
for (let i = 0; i < Object.keys(obj).length; i++)
for (const prop in obj) {
if (obj[prop].index == i) console.log(obj[prop].content);
}
На самом деле он не гарантирует порядок, но, скорее всего, он упорядочивает путем вставки элементов, но в некоторых случаях порядок подвержен изменениям. Например, если вы используете Object. Ключи и объект. Ценности и результаты их не одинаковы в первую очередь. Вероятный
Решением для сохранения порядка является сохранение ключей и использование этих ключей для доступа к объекту в первом порядке.
Что-то вроде этого:
MergeFlatedObjValue(obj){
const myJSONString = JSON.stringify(obj);
const regexp = /(?<!:)("[\w.]+")/g;
let matches = myJSONString.matchAll(regexp);
var MergeFlatedObjValue="";
for (const match of matches) {
var matched=match[0];
matched=matched.replace(/['"]+/g, '');
MergeFlatedObjValue+=obj[matched]+"#";
}
return MergeFlatedObjValue;
}
Из стандарта JSON:
Объект - это неупорядоченный набор из нуля или более пар имя / значение, где имя - это строка, а значение - строка, число, логическое значение, значение null, объект или массив.
(акцент мой).
Итак, нет, вы не можете гарантировать заказ.