Порядок элементов в цикле "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.

В IE6 порядок не гарантируется.

Это не отвечает на вопрос как таковой, но предлагает решение основной проблемы.

Предполагая, что вы не можете полагаться на порядок сохранения, почему бы не использовать массив объектов с ключом и значением в качестве свойства?

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". Так что будьте осторожны.

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