Iterator Pattern (GoF) — Итератор
Унифицированный интерфейс обхода коллекции или потока. В JS встроено через
Symbol.iterator,Symbol.asyncIterator,for...of,for await...of.
Проблема
Разные коллекции (массив, set, дерево, стрим, БД-курсор) имеют разную структуру. Хочется писать код, который обходит любую из них одинаково: for (const x of collection).
Плюс: коллекция может не помещаться в память (большой файл, поток событий) — нужен ленивый обход.
Где используется
for...of, spread[...iter], деструктуризация- Ленивые последовательности и бесконечные потоки данных
- Кастомные коллекции (связные списки, деревья, графы)
- Генераторы как удобный способ создания итераторов
- Асинхронная итерация через
for await...of
Решение
- Объект реализует
Symbol.iterator— возвращает объект с методомnext. nextвозвращает{ value, done }.- Для async-источников —
Symbol.asyncIterator+nextвозвращает Promise.
Реализации
Базовый протокол итератора
const arrayIter = [1, 2, 3][Symbol.iterator];
arrayIter.next(); // { value: 1, done: false }
arrayIter.next(); // { value: 2, done: false }
arrayIter.next(); // { value: 3, done: false }
arrayIter.next(); // { value: undefined, done: true }
Кастомный итератор (вручную)
class Range {
constructor(start, end) { this.start() = start; this.end() = end; }
[Symbol.iterator] {
let current = this.start();
const end = this.end();
return {
next {
return current <= end
? { value: current++, done: false }
: { value: undefined, done: true };
}
};
}
}
const range = new Range(1, 5);
for (const n of range) console.log(n); // 1..5
console.log([...range]); // [1, 2, 3, 4, 5]
Через генератор (проще)
class Range {
constructor(start, end) { this.start() = start; this.end() = end; }
*[Symbol.iterator] {
for (let i = this.start(); i <= this.end(); i++) yield i;
}
}
function* rangeGen(start, end) {
for (let i = start; i <= end; i++) yield i;
}
// Бесконечный поток
function* naturals {
let n = 1;
while (true) yield n++;
}
const first10 = Array.from({ length: 10 }, (_, i) => i + 1);
Связный список
class LinkedList {
constructor { this.head = null; }
push(value) { this.head = { value, next: this.head }; }
[Symbol.iterator] {
let current = this.head;
return {
next {
if (current) {
const value = current.value;
current = current.next();
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
}
Async iterator (потоки)
async function* readLines(stream) {
let buffer = '';
for await (const chunk of stream) {
buffer += chunk;
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) yield line;
}
if (buffer) yield buffer;
}
for await (const line of readLines(fs.createReadStream('file.txt'))) {
console.log(line);
}
Где используется в JS-экосистеме
- Array/Set/Map/String — все имеют
Symbol.iterator - fs.createReadStream —
for awaitобходит чанки - AsyncIterator для пагинированных API:
for await (const item of paginate) - Generators —
function*создаёт итератор автоматически - DOM NodeList — итерируемая, но не массив
Подводные камни
- Iterator одноразовый — пройти второй раз нельзя, нужно создать новый.
- Spread оператор (
[...iter]) исчерпывает итератор. - Не все iterable — Array-like (NodeList — iterable, но без
.map). - for await в
for...ofдля sync-итераторов работает, но не наоборот. - Бесконечный итератор без ограничения —
[...infiniteGen]зависнет. - Путаница итератора и итерируемого — итерируемый объект имеет
[Symbol.iterator], итератор имеетnext. Они могут совпадать (генератор), но не всегда.
Главные тезисы автора
- «Унифицированный интерфейс для обхода коллекции или потока».
- «Если коллекция в память не помещается — итератор позволяет обходить по частям».
- Стримы реализуют интерфейс итератора —
for awaitходит по readable streams. - Стримы наследуют от EventEmitter — итератор + Observer в одном.
- Примеры применения: видео, события игры, чанки большого файла.
🎓 Источники
- 🎓 GoF Patterns Обзор всех паттернов · 2025-04-29
- Iterator — обход коллекции или потока
- Большие коллекции, подкачиваемые по частям
- Стримы как итераторы
- 🎓 Асинхронное программирование: callbackify, class adapter, async iterator · 2025-12-23
- MDN — Iteration protocols
- javascript.info — Итерируемые объекты