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

Как написать итератор без генератора?

2.3 Middle🔥 161 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

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

Создание итератора без генератора

Итератор - это объект, который последовательно возвращает значения через метод next(). В JavaScript можно создать итератор без использования function* (генератора), реализовав протокол итератора.

Протокол итератора

Объект должен иметь метод Symbol.iterator, который возвращает объект с методом next(). Метод next() должен возвращать объект с полями value и done:

class CounterIterator {
  constructor(max) {
    this.max = max;
    this.current = 0;
  }

  [Symbol.iterator]() {
    return this;  // Сам объект является итератором
  }

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

const counter = new CounterIterator(3);
for (const num of counter) {
  console.log(num);  // 0, 1, 2
}

Итератор в отдельном классе

Можно отделить коллекцию от логики итерации:

class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    return new RangeIterator(this.start, this.end);
  }
}

class RangeIterator {
  constructor(start, end) {
    this.current = start;
    this.end = end;
  }

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

const range = new Range(1, 5);
console.log([...range]);  // [1, 2, 3, 4]
for (const num of range) {
  console.log(num);
}

Итератор с состоянием

Для более сложных сценариев, например обхода дерева:

class TreeNode {
  constructor(value, left = null, right = null) {
    this.value = value;
    this.left = left;
    this.right = right;
  }
}

class TreeIterator {
  constructor(root) {
    this.stack = root ? [root] : [];
  }

  [Symbol.iterator]() {
    return this;
  }

  next() {
    if (this.stack.length === 0) {
      return { done: true };
    }

    const node = this.stack.pop();
    if (node.right) this.stack.push(node.right);
    if (node.left) this.stack.push(node.left);

    return { value: node.value, done: false };
  }
}

const tree = new TreeNode(1,
  new TreeNode(2),
  new TreeNode(3)
);

for (const val of new TreeIterator(tree)) {
  console.log(val);  // 1, 2, 3 (или другой порядок в зависимости от реализации)
}

Фильтрующий итератор

Пример итератора, который фильтрует элементы во время итерации:

class FilterIterator {
  constructor(items, predicate) {
    this.items = items;
    this.predicate = predicate;
    this.index = 0;
  }

  [Symbol.iterator]() {
    return this;
  }

  next() {
    while (this.index < this.items.length) {
      const item = this.items[this.index++];
      if (this.predicate(item)) {
        return { value: item, done: false };
      }
    }
    return { done: true };
  }
}

const numbers = [1, 2, 3, 4, 5, 6];
const evenIterator = new FilterIterator(numbers, n => n % 2 === 0);

for (const num of evenIterator) {
  console.log(num);  // 2, 4, 6
}

Отличие от генератора

Генератор function* более лаконичен, но кастомный итератор дает полный контроль и лучше работает с классами и сложной логикой управления состоянием.

Как написать итератор без генератора? | PrepBro