Порядок итерации for.in не путем вставки (больше?)

Согласно моим исследованиям, порядок ключей в цикле for..in должен быть неопределенным/ненадежным, но, если его не нарушать, он должен быть в порядке вставки, но это не так:

Я извлекаю этот объект данных из базы данных, упорядоченный по имени:

var travel = {  
    '2': { name: 'bus',  price: 10 },  
    '3': { name: 'foot', price: 0 },  
    '1': { name: 'taxi', price: 100 }  
}  
for (way in travel) console.log( travel[way].name ) // => taxi, bus, foot  

Ключи упорядочиваются по номерам (во всех Chrome, Firefox и Edge). Почему?

И (поскольку я был неправ) как я могу перебрать их, заказанные .name?


person T4NK3R    schedule 04.03.2017    source источник
comment
Если вам нужно их заказать, используйте sort() на Object.values().   -  person Sirko    schedule 04.03.2017
comment
я предполагаю, что может происходить какая-то внутренняя оптимизация, которая выглядит как массив   -  person Daniel A. White    schedule 04.03.2017
comment
да - если вы добавите ключ, который не является числом, он будет работать как положено   -  person Daniel A. White    schedule 04.03.2017
comment
в результате порядок не определен и на него никогда нельзя полагаться   -  person Jaromanda X    schedule 04.03.2017


Ответы (2)


Согласно моим исследованиям, порядок ключей в цикле for..in должен быть неопределенным/ненадежным.

Неопределенный, да.

  • но, если оставить его нетронутым, должен быть в порядке вставки

Нет, вы были правы в первый раз: это не определено. Даже в ES2015 (он же «ES6») и более поздних версиях, которые обеспечивают порядок свойств для некоторых других операций, более старые операции for-in и Object.keys не обязаны следовать порядку, определенному для новых.

В этих других операциях (Object.getOwnPropertyNames, JSON.serialize, ...) порядок (определенное здесь) не является чисто порядком вставки: свойства, чьи имена являются индексами массива в соответствии с определением спецификации*, идут первыми в числовом порядке. Большинство основных движков JavaScript обновили свою обработку for-in, чтобы она соответствовала их обработке этих новых операций (многие уже по-разному обрабатывали индексы массива, но они различались в зависимости от того, помещали ли они их перед индексами, не являющимися массивами, или после), но опять же , он не определен, и на него не следует полагаться.

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

Вот пример использования Map:

const map = new Map([
  ['2', { name: 'bus',  price: 10 }],
  ['3', { name: 'foot', price: 0 }],
  ['1', { name: 'taxi', price: 100 }]
]);
for (const entry of map.values()) { // bus, foot, taxi
  console.log(entry.name);
}


* Определение спецификации «индекс массива»:

Целочисленный индекс — это ключ свойства со строковым значением, представляющий собой каноническую числовую строку (см. 7.1.16), чье числовое значение равно +0 или положительному целому числу ≤ 253-1<. /суп>. Индекс массива — это целочисленный индекс, числовое значение которого i находится в диапазоне +0 ≤ i ‹ 232–1.

person T.J. Crowder    schedule 04.03.2017
comment
Спасибо за фантастический ответ! Карта выглядит как мой новый лучший друг :) - person T4NK3R; 04.03.2017

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

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

person Alexander Lebedev    schedule 04.03.2017
comment
Я не люблю надеяться на лучший подход - слишком стар для этого :) Карта есть! - person T4NK3R; 04.03.2017