Елементи впорядковуються в циклі «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"' у найбільш пристойних браузерах?


61
Зрештою, вони б протестували лише підмножину потенційних браузерів та варіантів. Не кажучи вже про майбутніх браузерах. Очевидно, неправильно вважати, що тест, який не виконується, дає будь-які конкретні докази.
Джеймс Х'юз

6
Я сумніваюся, моя власна обмежена здатність JavaScript буде кращою, ніж натовп SO. До того ж хто знає, що там ховається дивний браузер? І ви можете побачити у відповіді, що в GChrome є помилка, яка не буде очевидною в моєму простому прикладі.
чакрит


Відповіді:


214

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

В даний час всі основні браузери переглядають властивості об'єкта в тому порядку, в якому вони були визначені. Chrome робить це також, за винятком кількох випадків. [...] Ця поведінка явно не визначена специфікацією ECMAScript. У розділі 12.6.4 ECMA-262:

Механіка перерахування властивостей ... залежить від реалізації.

Однак специфікація сильно відрізняється від реалізації. Усі сучасні реалізації 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/isissue/detail?id=164
Tim Down

19
"Використовуйте масив, якщо для вас важливий порядок": Що робити, коли ви використовуєте JSON?
HM2K

11
@ HM2K, Те саме, специфікація говорить: "Об'єкт - це не упорядкована колекція нульових і більше пар імен / значень". JSON - це не JavaScript: серверу не потрібно (і, мабуть, не буде) поважати порядок, який ви його передаєте.
Боргар

7
Firefox, починаючи з 21-ї версії, більше не дотримується порядку вставки.
Док. Давлуз

2
Ця відповідь є неправдивою у 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] 

16
в чому тут проблема? Точно, як каже специфікація, це не упорядкований список пар ключів / валів.
nickf

5
nickf: реалізації мають рацію в тому, що вони роблять те, що говорить специфікація. Я думаю, що всі згодні з тим, що специфікація JavaScript - це ... ну, я не хочу вживати слово "неправильно", а як щодо "надзвичайно дурний і дратівливий"? : P
коробці

10
@boxed: Подумайте про об'єкти як хеш-карту (таблицю / будь-яку іншу) У більшості мов (Java, Python тощо) подібні структури даних не сортуються. Тож не дивно, що це той самий випадок і в JavaScript, і це, звичайно, не робить специфікацію неправильною чи дурною.
Фелікс Клінг

9
Я, чесно кажучи, не бачу сенсу цієї відповіді (вибачте). Порядок, в якому повторюються властивості, є детальною інформацією про реалізацію, і браузери використовують різні двигуни JavaScript, тому очікується, що порядок відрізняється. Це не зміниться.
Фелікс Клінг

2
У найсучаснішому варіанті реалізації ви отримаєте цілі властивості в порядку числового значення завдяки підтримці реального масиву, а названі властивості будуть отримані в порядку вставки через приховану формулювання класів / фігур. Що може відрізнятись, чи вони перераховують спочатку цілі властивості чи спочатку називають властивості. Додавання deleteцікаве тим, що принаймні у V8 воно моментально спричиняє резервне копіювання об'єкта хеш-таблицею. Однак хеш-таблиця у V8 зберігається в порядку вставки. Найцікавіший результат тут - IE, мені цікаво, яке
потворство

27

З специфікації мови ECMAScript , розділ 12.6.4 (у for .. inциклі):

Механіка перерахування властивостей залежить від реалізації. Порядок перерахування визначається об'єктом.

І розділ 4.3.3 (визначення поняття "Об'єкт"):

Це невпорядкована колекція властивостей, кожна з яких містить примітивне значення, об'єкт чи функцію. Функція, що зберігається у властивості об'єкта, називається методом.

Я думаю, це означає, що ви не можете покладатися на перераховані властивості у послідовному порядку в усіх реалізаціях JavaScript. (Було б поганим стилем у будь-якому випадку покладатися на деталі конкретної мови.)

Якщо ви хочете, щоб ваше замовлення було визначено, вам потрібно буде реалізувати щось, що його визначає, як масив клавіш, який ви сортуєте, перш ніж отримати доступ до об'єкта з ним.


10

Елементи об’єкта, які для / у перерахуваннях, є властивостями, у яких не встановлено прапор DontEnum. Стандарт ECMAScript, також Javascript, прямо говорить, що "Об'єкт - це не упорядкована колекція властивостей" (див. Http://www.mozilla.org/js/language/E262-3.pdf розділ 8.6).

Це не буде стандартом, відповідним стандартам (тобто безпечним), припускаючи, що всі реалізації Javascript будуть перераховані в порядку декларації.


2
тому він задає питання, а не просто припускає: p
Джеймі Пат

4

Порядок ітерації також плутають щодо видалення властивостей, але в цьому випадку лише з 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/isissue/detail?id=164 є будь-якою ознакою.


3

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


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
    } // ...
];

Тепер, ви повинні переконатися, що ключі є унікальними (якщо припустити, що це теж важливо для вас. Крім того, пряме адресація змін, а для (... в ...) тепер повертає індекси як "ключі".

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.