Порядок элементов в цикле "for (... in...)"

Происходит ли цикл "for... in" в Javascript через хэш-таблицы/элементы в том порядке, в котором они объявлены? Есть ли браузер, который не делает это в порядке?
Объект, который я хочу использовать, будет объявлен один раз и никогда не будет изменен.

Предположим, что у меня есть:

var myObject = { A: "Hello", B: "World" };

И я также использую их в:

for (var item in myObject) alert(item + " : " + myObject[item]);

Могу ли я ожидать "A:" "Всегда" перед "B:" Мир "в большинстве достойных браузеров?

Ответ 1

Цитата Джона Резига:

В настоящее время все основные браузеры перекрывают свойства объекта в порядке, указанном в которые они были определены. Chrome тоже делает это, за исключением нескольких случаев. [...] Это поведение явно осталось undefined по спецификации 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"

Технические характеристики этого менее важны, чем тот факт, что это может измениться в любое время. Не полагайтесь на все, оставаясь таким образом.

Короче: Используйте массив, если для вас важен порядок.

Ответ 2

Накануне этого через год...

Это 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);

gist или test в текущем браузере

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] 

Ответ 3

Из Спецификация языка ECMAScript, раздел 12.6.4 (в цикле for .. in):

Механизм перечисления свойств зависит от реализации. Порядок перечисления определяется объектом.

И раздел 4.3.3 (определение "Объект" ):

Это неупорядоченный набор свойств, каждый из которых содержит примитивное значение, объект или функцию. Функция, хранящаяся в свойстве объекта, называется методом.

Я предполагаю, что это означает, что вы не можете полагаться на свойства, которые перечислены в последовательном порядке в реализациях JavaScript. (В любом случае, это был бы плохой стиль, чтобы полагаться на специфические для конкретной реализации детали языка.)

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

Ответ 4

Элементы объекта, который для/в перечислении являются свойствами, которые не имеют установленного флага DontEnum. В стандарте ECMAScript, так называемом Javascript, явно указано, что "Object - это неупорядоченный набор свойств" (см. http://www.mozilla.org/js/language/E262-3.pdf раздел 8.6).

Это не будет соответствовать стандартам (то есть безопасно), чтобы предположить, что все реализации Javascript будут перечисляться в порядке декларации.

Ответ 5

Порядок итераций также путают с удалением свойств, но в этом случае только с 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 любое указание.

Ответ 6

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

Ответ 7

Приложению нельзя доверять. 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.

Ответ 8

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

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

var myArray = [
    {
        'key'   : 'key1'
        'value' : 0
    },
    {
        'key'   : 'key2',
        'value' : 1
    } // ...
];

Теперь вам нужно убедиться, что ключи уникальны (при условии, что это также важно для вас. Кроме того, прямая адресация изменений, а for (... in...) теперь возвращает индексы как "ключи".

> console.log(myArray[0].key);
key1

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
См. Перо для (... in...) адресации в порядке JDQ (@JDQ) на CodePen.