← Назад к вопросам

Можно ли определить поведение for…of в созданном объекте?

1.7 Middle🔥 181 комментариев
#JavaScript Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Да, безусловно, можно. Поведение for…of в созданном объекте определяется путем реализации в этом объекте итератора. Это мощный механизм JavaScript, который позволяет настраивать итерацию по любым структурам данных.

Что такое итератор?

Итератор — это объект, который реализует специфичный протокол итерации. Этот протокол требует, чтобы у объекта был метод с ключом Symbol.iterator. При вызове, этот метод должен возвращать объект-итератор, у которого есть метод next().

Каждый вызов next() возвращает объект с двумя свойствами:

  • value — текущее значение последовательности.
  • done — булево значение, указывающее, завершена ли итерация (true — завершена).

Как сделать объект итерируемым

Чтобы объект можно было использовать в цикле for...of, нужно определить для него свойство Symbol.iterator. Рассмотрим на практических примерах.

Пример 1: Итерация по диапазону чисел

Создадим объект range, который будет генерировать последовательность чисел от from до to.

const range = {
  from: 1,
  to: 5,

  // 1. Вызов for...of сначала вызывает этот метод (Symbol.iterator)
  [Symbol.iterator]() {
    // ...он возвращает объект-итератор:
    // 2. Далее for...of работает только с этим возвращённым объектом
    return {
      current: this.from,
      last: this.to,

      // 3. Метод next() вызывается на каждой итерации цикла for...of
      next() {
        if (this.current <= this.last) {
          // 4. Он должен вернуть значение в виде объекта {value, done}
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

// Теперь работает!
for (let num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

Пример 2: Более компактная реализация

Часто сам объект может выступать в роли итератора, упрощая код.

const range = {
  from: 10,
  to: 13,

  [Symbol.iterator]() {
    this.current = this.from;
    return this; // Возвращаем сам объект, который и будет итератором
  },

  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ };
    } else {
      return { done: true };
    }
  }
};

for (let num of range) {
  console.log(num); // 10, 11, 12, 13
}

Связь с другими возможностями языка

Объект, реализующий протокол итератора, автоматически получает совместимость с другими частями языка, которые используют итерируемые объекты:

  • Оператор spread (...): console.log([...range]); // [1, 2, 3, 4, 5]
  • Деструктуризация: const [first, second] = range;
  • Array.from(): const arr = Array.from(range);
  • Promise.all(), Promise.race() (работают с итерируемыми коллекциями промисов).
  • Map, Set, arguments — все они являются итерируемыми по умолчанию.

Генераторы — элегантная альтернатива

Для создания итераторов существует более современный и лаконичный синтаксис — функции-генераторы (с ключевым словом function* и yield).

const range = {
  from: 5,
  to: 7,
  *[Symbol.iterator]() { // Краткая запись для генератора
    for(let value = this.from; value <= this.to; value++) {
      yield value;
    }
  }
};

for (let num of range) {
  console.log(num); // 5, 6, 7
}

Генератор автоматически создает объект, соответствующий протоколу итератора. Ключевое слово yield возвращает значение и приостанавливает выполнение функции до следующей итерации.

Заключение

Итак, определить поведение for…of для созданного объекта не только можно, но и часто нужно. Это достигается путем:

  1. Реализации протокола итератора через метод [Symbol.iterator]().
  2. Создания объекта-итератора с методом next().
  3. (Опционально) Использования генераторов для значительного упрощения кода.

Этот подход лежит в основе работы встроенных коллекций (Array, Map, Set) и открывает путь к созданию абстракций для обхода любых данных — от древовидных структур и графов до потоков и пагинированных API-ответов. Это фундаментальная концепция, которая превращает JavaScript из языка, работающего только со встроенными типами, в язык, позволяющий создавать высокоуровневые, выразительные и эффективные абстракции для обработки данных.