Генераторы в JavaScript: Symbol.iterator и for…of

JavaScript предоставляет гибкий инструмент — генераторы: функции, последовательно выдающие результаты. Рассмотрим их функционирование и применение.

Создание функции-генератора

Для создания функции-генератора после ключевого слова function ставится звёздочка (*). Звёздочка указывает, что функция будет генератором. Например:

function* strGenerator() {
  // ...
}

Внутри генератора используется ключевое слово yield. yield порционно выдает результат. Например, для вывода строки «Hello» посимвольно:

function* strGenerator() {
  yield 'H';
  yield 'e';
  yield 'l';
  yield 'l';
  yield 'o';
}

Работа с генератором

Вызов функции-генератора возвращает объект-итератор с методом next(). Каждый вызов next() возвращает объект с двумя свойствами: value (выданное значение) и done (флаг завершения генератора).

const str = strGenerator();
console.log(str.next()); // { value: 'H', done: false }
console.log(str.next()); // { value: 'e', done: false }
console.log(str.next().value); // 'l'
// ... и так далее
console.log(str.next()); // { value: undefined, done: true }

После обработки всех значений yield, done становится true, а value — undefined.

Генераторы с параметрами и циклом

Генераторы могут принимать параметры:

function* numberGen(n = 10) {
  for (let i = 0; i < n; i++) {
    yield i;
  }
}

const numbers = numberGen(7);
console.log(numbers.next()); // { value: 0, done: false }
// ... и так далее

Этот пример демонстрирует генератор последовательности чисел от 0 до n-1.

Итераторы и Symbol.iterator

Цикл for…of предназначен для итерации по итерируемым объектам. Он использует специальный символ Symbol.iterator. Если у объекта есть свойство Symbol.iterator, возвращающее итератор с методом next(), то for…of может по нему итерироваться.

for (let key of "Hello") {
  console.log(key); // Выводит посимвольно: H, e, l, l, o
}

const fibonacci = [1, 1, 2, 3, 5, 8, 13];
for (let num of fibonacci) {
  console.log(num); // Выводит элементы массива
}

Строки и массивы имеют встроенное свойство Symbol.iterator. Для собственных объектов его необходимо добавить:

const iteratorWithSymbol = {
  *[Symbol.iterator]() {
    yield 10;
    yield 20;
    yield 30;
  }
};

for (let value of iteratorWithSymbol) {
  console.log(value); // Выводит 10, 20, 30
}

Теперь iteratorWithSymbol итерируется циклом for…of. *[Symbol.iterator]() — это генераторная функция, автоматически добавляющая необходимое свойство.

Генераторы и for…of

Генераторы автоматически имеют свойство Symbol.iterator, поэтому их можно использовать непосредственно в цикле for…of:

function* iterGen(n = 10) {
  for (let i = 0; i < n; i++) {
    yield i;
  }
}

for (let i of iterGen(6)) {
  console.log(i); // Выводит 0, 1, 2, 3, 4, 5
}

Генераторы — мощный инструмент для создания итераторов в JavaScript. Они упрощают работу с последовательностями данных и хорошо взаимодействуют с циклом for…of благодаря встроенному свойству Symbol.iterator. Выбор между генераторами и ручной реализацией итератора зависит от задачи.

Что будем искать? Например,программа