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

perfect

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

2 ответа

perfect

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

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

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

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

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


perfect

tl;dr:

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

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

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

<i>IterationStatement: <b>for</b> <b>(</b> LeftHandSideExpression <b>in</b> Expression <b>)</b> Statement</i>
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(LeftHandSideExpression, Statement, keyResult, assignment, labelSet).
<i>IterationStatement: <b>for</b> <b>(</b> <b>var</b> ForBinding <b>in</b> Expression <b>)</b> Statement</i>
  1. Let keyResult be ? ForIn/OfHeadEvaluation(« », Expression, enumerate).
  2. Return ? ForIn/OfBodyEvaluation(ForBinding, Statement, keyResult, varBinding, labelSet).
<i>IterationStatement: <b>for ( </b>ForDeclaration <b>in</b> Expression <b>)</b> Statement</i>
  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]]. Если объект не строка или ********** вызывается 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, в порядке добавления в объект. А так же после этого в том же порядке добавятся перечисляемые свойства из прототипа.

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

licensed under cc by-sa 3.0 with attribution.