Порядок элементов в цикле «for (… in…)»

205

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

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

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

И я в дальнейшем использую их в:

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

Можно ли ожидать, что «A:« Hello »» всегда будет стоять перед «B:« World »» в большинстве приличных браузеров?

chakrit
источник
61
Так как они будут тестировать только часть потенциальных браузеров и вариантов. Не говоря уже о любых будущих браузерах. Совершенно неверно предполагать, что безотказный тест дает какие-либо конкретные доказательства.
Джеймс Хьюз
6
Я сомневаюсь, что мои собственные ограниченные возможности javascript будут лучше, чем у SO. Кроме того, кто знает, какой странный браузер скрывается там? И вы можете увидеть в ответе, что в GChrome есть ошибка, которая не будет очевидна в моем простом примере.
чакрит
возможный дубликат Гарантирует ли JavaScript свойство Property Order?
Берги

Ответы:

214

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

В настоящее время все основные браузеры перебирают свойства объекта в том порядке, в котором они были определены. 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"

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

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

Боргар
источник
3
На самом деле, нет. В Chrome не реализован тот же порядок, что и в других браузерах: code.google.com/p/v8/issues/detail?id=164
Tim Down
20
«Используйте массив, если порядок важен для вас»: как насчет использования JSON?
HM2K
11
@ HM2K, То же самое, спецификация говорит: «Объект - это неупорядоченный набор из нуля или более пар имя / значение». JSON - это не JavaScript: сервер не должен (и, вероятно, не будет) соблюдать порядок, в котором вы его передаете.
Боргар
7
Firefox, начиная с 21 версии, похоже, больше не соответствует порядку вставки.
Док Давлуз
3
Этот ответ неверен в ES2015.
Бенджамин Грюнбаум
54

Натыкаясь на это год спустя ...

Это 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] 
dvdrtrgn
источник
16
в чем тут проблема? Точно, как сказано в спецификации, это неупорядоченный список пар ключ / вал.
Nickf
5
nickf: реализации верны в том, что они делают то, что говорит спецификация. Я думаю, что все согласны с тем, что спецификация javascript ... ну, я не хочу использовать слово "неправильно", а как насчет "чрезвычайно глупо и раздражает"? : P
штучной упаковке
10
@boxed: Думайте об объектах как о хэш-карте (таблица / что угодно). В большинстве языков (Java, Python и т. Д.) Такие типы структур данных не сортируются. Поэтому неудивительно, что это то же самое относится и к JavaScript, и это, безусловно, не делает спецификации неправильными или глупыми.
Феликс Клинг
9
Я, честно говоря, не вижу смысла этого ответа (извините). Порядок, в котором свойства повторяются, является деталью реализации, и браузеры используют разные механизмы JavaScript, поэтому ожидается, что порядок отличается. Это не изменится.
Феликс Клинг
2
В современной реализации вы получите целочисленные свойства в порядке числовых значений благодаря тому, что они поддерживаются реальным массивом, а именованные свойства будут получены в порядке вставки благодаря скрытой формулировке классов / форм. То, что может измениться, перечисляет ли они целочисленные свойства сначала или названные свойства сначала. Добавление deleteинтересно, потому что, по крайней мере в V8, оно мгновенно вызывает резервное копирование объекта с помощью хеш-таблицы. Однако хеш-таблица в V8 хранится в порядке вставки. Самый интересный результат здесь - это IE, мне интересно, какое уродство они делают, чтобы осуществить это ...
Esailija
28

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

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

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

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

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

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

Томалак
источник
10

Элементы объекта, которые для / in перечисляют, являются свойствами, для которых не установлен флаг DontEnum. Стандарт ECMAScript, также известный как Javascript, прямо говорит, что «Объект - это неупорядоченный набор свойств» (см. Http://www.mozilla.org/js/language/E262-3.pdf раздел 8.6).

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

Адам Райт
источник
2
именно поэтому он задает вопрос, вместо того, чтобы просто предположить: p
Джейми Пэйт
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 является каким-либо указанием.

Бретт Замир
источник
2

Заказу нельзя доверять. 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.

Кубер Сапарев
источник
1

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

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

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 .

JDQ
источник