Одно из самых важных дополнений ES6 - возможность повторения.

Итерируемый объект - это особый вид объекта, который реализует итерационный протокол, что означает, что у них есть специальное свойство с ключом [Symbol.iterator] и значением функции, возвращающей итератор.

Итератор - это объект, реализующий протокол итератора. У него должен быть метод next(), который возвращает объект с двумя свойствами:

  • value может быть любым значением JavaScript,
  • done - логическое значение, указывающее, завершил ли итератор свою последовательность.

Итак, итераторы и итераторы имеют следующую сигнатуру с использованием интерфейсов TypeScript:

interface Iterable {
    [Symbol.iterator]() : Iterator;
}
interface Iterator {
    next() : IteratorResult;
    return?(value? : any) : IteratorResult;
}
interface IteratorResult {
    value: any;
    done: boolean;
}

Эти протоколы позволяют нам перебирать набор значений, связанных с нашим объектом.

Хорошая метафора для понимания итераций - если мы возьмем итеративный физический объект, например книгу. В этом случае итератором будет закладка, которая хранит ссылку на то, где мы находимся в процессе итерации. Следующий метод - это то, что на самом деле выполняет переход к следующей итерации, которой в данном случае является переворачивание страницы.

В ES6 многие встроенные типы получили свойство итератора. Например:

  • Струны
  • Массивы
  • Карты
  • Наборы
  • NodeLists
  • и объект argument

все повторяются.

Мы можем перебрать их с помощью цикла for… of, использовать его с оператором распространения или с деструктурирующим присваиванием.

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

Примеры простых функций итератора

Строки по умолчанию являются итерациями, но мы можем переопределить итератор строк по умолчанию, просто добавив нашу собственную функцию итератора в свойство [Symbol.iterator]. Допустим, мы хотим перебрать символы строки в обратном направлении:

Обратите внимание, что мы должны использовать конструктор String, чтобы избежать автоматической упаковки. Кроме того, мы должны использовать стрелочную функцию в следующем методе, чтобы this ссылалась на наш строковый объект.

Мы используем закрытую переменную i в качестве состояния наших итераторов и возвращаем символы строки от конца до первого символа. Достигнув этого места, мы возвращаем наш объект IteratorResult с установленным флагом done в значение true, сигнализируя об окончании итерационного процесса.

Объекты в JavaScript не являются повторяемыми по умолчанию по ряду причин, но это не означает, что мы не можем добавить к ним собственный итератор. Например, если мы хотим перебрать значения объекта в порядке возрастания:

Мы также можем легко определить итерируемый диапазон следующим образом:

Что, если мы хотим сделать связанный список повторяемым? Давайте возьмем простую реализацию связанного списка:

Мы также можем просто добавить наш итератор в класс LinkedList:

Ссылки:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
https://exploringjs.com/es6/ch_iteration.html