По какому принципу цикл FOR IN обходит массив?

273
23 декабря 2016, 16:03

Меня интересует порядок обращения к элементам в массиве с помощью цикла FOR IN. А то есть с какого элемента начинает и каков принцип выборки.

Answer 1

Цикл for...in проходит через перечисляемые свойства объекта, в произвольном порядке

for...in не следует использовать для Array, где важен порядок индексов.
Индексы массива перечисляемые свойства с целочисленными именами, а в остальном аналогичны объектам. Нет гарантии, что for...in будет возвращать индексы в нужном порядке и вернёт все перечисляемые свойства, включая имеющие нецелочислененные имена и наследуемые

Вдобавок, порядок обхода зависит от реализации ECMAScript браузером

стандарт ECMAScript оставляет порядок итерации по свойствам объектов на усмотрение реализующей стороны

Для массивов предпочтительней использовать простой for

Answer 2

tl;dr:

Сначала переберутся числовые ключи соответствующие индексам начиная с 0, затем все остальные строковые ключи.

Немного спецификации

Для различных вариантов for..in вызывается одна и та же внутренняя функция

IterationStatement: for ( LeftHandSideExpression in Expression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement, keyResult, assignment, labelSet).
IterationStatement: for ( var ForBinding in Expression ) Statement
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(ForBinding, Statement, keyResult, varBinding, labelSet).
IterationStatement: for ( ForDeclaration in Expression ) Statement
  1. Let keyResult be the result of performing ? ForIn/OfHeadEvaluation(BoundNames of ForDeclaration, Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(ForDeclaration, Statement, keyResult, lexicalBinding, labelSet).

Если перейти к этой функции можно найти алгоритм, который определяет получение свойств. Интересен пункт 6

  1. Если iterationKind is enumerate, то
    1. Если exprValue.[[Value]] равен null или undefined, тогда
      • Вернуть Completion{[[Type]]: break, [[Value]]: empty, [[Target]]: empty}.
    2. Пусть obj будет ToObject(exprValue).
    3. Вернуть EnumerateObjectProperties(obj).

Именно в функции EnumerateObjectProperties(obj) и осуществляется перебор.

В ее описании можно найти следующий пункт:

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

В нашем случае интересны следующие правила:

  1. The enumerable property names of prototype objects must be obtained by invoking EnumerateObjectProperties passing the prototype object as the argument.
  2. EnumerateObjectProperties must obtain the own property keys of the target object by calling its [[OwnPropertyKeys]] internal method.
  1. Имена перечисляемых свойств прототипа должны быть получены вызовом EnumerateObjectProperties с передачей ей прототипа текущего объекта как аргумента.
  2. EnumerateObjectProperties должен получить собственный имена свойств целевого объекта с помощью вызова внутреннего метода [[OwnPropertyKeys]]

И наконец, пришли к главному: [[OwnPropertyKeys]]. Если объект не строка или TypedArray вызывается OrdinaryOwnPropertyKeys (O) в которой выполняются следующие шаги:

  1. keys = new empty List.
  2. Для каждого свойства P, из объекта O, которое является целым числом больше либо равным 0, в порядке возрастания:
    1. Добавить P в конец keys.
  3. Для каждого свойства P, из объекта O, которое является Строкой, но не целым числом больше либо равным 0, в порядке создания свойств
    1. Добавить P в конец keys.
  4. Для каждого свойства P, из объекта O, имеющего тип Symbol, в порядке создания
    1. Добавить P в конец keys.
  5. Вернуть keys.

В итоге в тело for..in попадет сначала целочисленные ключи больше либо равные 0 в порядке возрастания, затем строковые, которые не могут быть представлены целым числом больше либо равным 0, в порядке добавления в объект. А так же после этого в том же порядке добавятся перечисляемые свойства из прототипа.

Можно отметить, что поведение массива, в данном случае ничем не отличается от поведения обычного объекта.

READ ALSO
Кросс-доменные запросы AJAX

Кросс-доменные запросы AJAX

Много мануалов на эту тему, но что-то пока ни один не дал нужного результата

1404
Не работает плагин gulp-rigger, почему?

Не работает плагин gulp-rigger, почему?

Установил этот плагин, пытаюсь импортировать файл, в indexhtml ввожу //= footer

1112
Существует ли аналог parseInt для слова в javascript?

Существует ли аналог parseInt для слова в javascript?

Числовое значение с сайта можно получить так:

393
Событие открытия модального окна Fancybox

Событие открытия модального окна Fancybox

Использую fancybox для открытия модальных оконПоявился вопрос

777