for..of
вызывает метод итератора Array, который описан в спецификации .
(2) Пусть итератор будет ObjectCreate (% ArrayIteratorPrototype%, « [[IteratedObject]], [[ArrayIteratorNextIndex]], [[ArrayIterationKind]]»).
(4) Установить внутренний слот итератора [[ArrayIteratorNextIndex]] на 0.
Затем, когда итератор повторяется, в 22.1.5.2.1% ArrayIteratorPrototype% .next: :
(6) Пусть index будет значением внутреннего слота [[ArrayIteratorNextIndex]] для O.
(10) Если индекс ≥ len, то
(10) (a) Установите значение внутреннего слота [[IteratedObject]] для O в значение undefined.
(10) (b) Возвращает CreateIterResultObject (undefined, true).
(11) Установите значение внутреннего слота [[ArrayIteratorNextIndex]] для O в index + 1 .
(создать объект результата итератора со значением array[index]
)
Другими словами - итератор выполняет итерацию, начиная с индекса 0, и увеличивает индекс на 1 каждый раз, когда вызывается .next()
. Он не проверяет, действительно ли массив имеет элемент с этим индексом (чего не будет у разреженного массива) - он просто проверяет, что индекс меньше .length
массива.
С for..in
, с другой стороны, все перечислимые свойства перебираются, а собственные перечисляемые свойства массива не включают разреженный массив указателей.
const sparse = [0, , 2];
console.log(sparse.hasOwnProperty('0'));
console.log(sparse.hasOwnProperty('1'));
Так что да, это предполагаемое поведение.