Порядок элементов в цикле "for (… in …)"
Цикл "for…in" в Javascript проходит через хеш-таблицы / элементы в порядке их объявления? Есть ли браузер, который не делает это по порядку?
Объект, который я хочу использовать, будет объявлен один раз и никогда не будет изменен.
Предположим, у меня есть:
var myObject = { A: "Hello", B: "World" };
И я в дальнейшем использую их в:
for (var item in myObject) alert(item + " : " + myObject[item]);
Можно ли ожидать, что "A: "Hello"" всегда будет стоять перед "B: "World"" в большинстве приличных браузеров?
10 ответов
В настоящее время все основные браузеры перебирают свойства объекта в том порядке, в котором они были определены. Chrome также делает это, за исключением пары случаев. [...] Это поведение явно не определено спецификацией ECMAScript. В ECMA-262, раздел 12.6.4:
Механика перечисления свойств... зависит от реализации.
Однако спецификация сильно отличается от реализации. Все современные реализации ECMAScript перебирают свойства объекта в том порядке, в котором они были определены. Из-за этого команда Chrome сочла это ошибкой и исправит ее.
Все браузеры соблюдают порядок определения, за исключением Chrome и Opera, которые соответствуют каждому нечисловому имени свойства. В этих двух браузерах свойства располагаются по порядку впереди первого нечислового свойства (это связано с тем, как они реализуют массивы). Порядок такой же для Object.keys
также.
Этот пример должен прояснить, что происходит:
var obj = {
"first":"first",
"2":"2",
"34":"34",
"1":"1",
"second":"second"
};
for (var i in obj) { console.log(i); };
// Order listed:
// "1"
// "2"
// "34"
// "first"
// "second"
Технические детали этого менее важны, чем тот факт, что это может измениться в любое время. Не полагайтесь на вещи, оставаясь такими.
Вкратце: используйте массив, если порядок важен для вас.
Натыкаясь на это год спустя...
Это 2012 год, и основные браузеры все еще различаются:
function lineate(obj){
var arr = [], i;
for (i in obj) arr.push([i,obj[i]].join(':'));
console.log(arr);
}
var obj = { a:1, b:2, c:3, "123":'xyz' };
/* log1 */ lineate(obj);
obj.a = 4;
/* log2 */ lineate(obj);
delete obj.a;
obj.a = 4;
/* log3 */ lineate(obj);
Суть или тест в текущем браузере
Safari 5, Firefox 14
["a:1", "b:2", "c:3", "123:xyz"]
["a:4", "b:2", "c:3", "123:xyz"]
["b:2", "c:3", "123:xyz", "a:4"]
Chrome 21, Opera 12, Node 0.6, Firefox 27
["123:xyz", "a:1", "b:2", "c:3"]
["123:xyz", "a:4", "b:2", "c:3"]
["123:xyz", "b:2", "c:3", "a:4"]
IE9
[123:xyz,a:1,b:2,c:3]
[123:xyz,a:4,b:2,c:3]
[123:xyz,a:4,b:2,c:3]
Из спецификации языка ECMAScript, раздел 12.6.4 (на for .. in
петля):
Механика перечисления свойств зависит от реализации. Порядок перечисления определяется объектом.
И раздел 4.3.3 (определение "Объект"):
Это неупорядоченный набор свойств, каждое из которых содержит примитивное значение, объект или функцию. Функция, хранящаяся в свойстве объекта, называется методом.
Я предполагаю, что это означает, что вы не можете полагаться на свойства, перечисляемые в согласованном порядке в реализациях JavaScript. (В любом случае было бы плохим стилем полагаться на специфические для реализации детали языка.)
Если вы хотите, чтобы ваш порядок был определен, вам нужно реализовать что-то, что его определяет, например, массив ключей, которые вы сортируете перед доступом к объекту с ним.
Элементы объекта, которые для / in перечисляют, являются свойствами, для которых не установлен флаг DontEnum. Стандарт ECMAScript, также известный как Javascript, прямо говорит, что "Объект - это неупорядоченная коллекция свойств" (см. http://www.mozilla.org/js/language/E262-3.pdf раздел 8.6).
Это не будет соответствовать стандартам (то есть безопасно) предполагать, что все реализации Javascript будут перечисляться в порядке объявления.
Порядок итерации также путают в отношении удаления свойств, но в этом случае только с IE.
var obj = {};
obj.a = 'a';
obj.b = 'b';
obj.c = 'c';
// IE allows the value to be deleted...
delete obj.b;
// ...but remembers the old position if it is added back later
obj.b = 'bb';
for (var p in obj) {
alert(obj[p]); // in IE, will be a, bb, then c;
// not a, c, then bb as for FF/Chrome/Opera/Safari
}
Желание изменить спецификацию, чтобы исправить порядок итераций, кажется довольно популярным среди разработчиков, если обсуждение на http://code.google.com/p/v8/issues/detail?id=164 является каким-либо указанием.
Как указано в других ответах, нет, порядок не гарантируется.
Если вы хотите выполнить итерацию по порядку, вы можете сделать что-то вроде:
let keys = Object.keys(myObject);
for (let key of keys.sort()) {
let value = myObject[key];
// Do what you want with key and value
}
Обратите внимание, что с точки зрения производительности это не оптимально, но это цена, когда вам нужен красивый алфавитный дисплей.
Заказу нельзя доверять. Opera и Chrome возвращают список неупорядоченных свойств.
<script type="text/javascript">
var username = {"14719":"A","648":"B","15185":"C"};
for (var i in username) {
window.alert(i + ' => ' + username[i]);
}
</script>
Код выше показывает B, A, C в Opera и C, A, B в Chrome.
Это не отвечает на вопрос как таковой, но предлагает решение основной проблемы.
Предполагая, что вы не можете полагаться на порядок сохранения, почему бы не использовать массив объектов с ключом и значением в качестве свойства?
var myArray = [
{
'key' : 'key1'
'value' : 0
},
{
'key' : 'key2',
'value' : 0
} // ...
];
Теперь вы должны убедиться, что ключи уникальны (при условии, что это также важно для вас. Кроме того, прямая адресация изменяется, а for (...in...) теперь возвращает индексы как "ключи".
> console.log(myArray[0].key);
key1
> for (let index in myArray) {console.log(myArray[index].value);}
0
1
См. Перо для (... in...) адресации в порядке JDQ ( @JDQ) на CodePen.
По состоянию на 2022 год,
Я только что узнал (к моему удивлению), что Chrome не соблюдает порядок определения. Он скорее сортирует его в алфавитном порядке, но не естественным образом . Например, ключ "a12" идет ДО "a3". Так что будьте осторожны.