跳到主要内容

迭代器模式

问题

什么是迭代器模式?JavaScript 的 Iterator 协议和 Iterable 协议是什么?如何实现自定义迭代器?Generator 和异步迭代器有什么应用场景?

答案

迭代器模式(Iterator Pattern)是 GoF 23 种经典设计模式之一,它提供一种方法顺序访问一个集合对象中的各个元素,而又不暴露该对象的内部表示。JavaScript 通过 Iterator ProtocolIterable Protocol 将这一模式内建到语言层面,使得 for...of、展开运算符、解构赋值等语法特性都依赖于统一的迭代协议。

更多关于 Symbol 和迭代器的基础知识,可参考 Symbol 与迭代器

GoF 经典定义与核心角色

经典定义

提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

在经典面向对象设计中,迭代器模式涉及两个核心角色:

角色职责JS 中的对应
Iterator(迭代器)定义访问和遍历元素的接口{ next(): { value, done } }
Aggregate(聚合)定义创建迭代器的接口{ [Symbol.iterator](): Iterator }

JavaScript 迭代器协议(Iterator Protocol)

一个对象只要实现了 next() 方法,并且该方法返回 { value, done } 形式的结果,就满足迭代器协议。

iterator-protocol.ts
// 迭代器接口定义
interface IteratorResult<T> {
value: T;
done: boolean;
}

interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>; // 可选:提前终止
throw?(error?: any): IteratorResult<T>; // 可选:抛出错误
}

// 手写一个最简单的迭代器
function createCountIterator(start: number, end: number): Iterator<number> {
let current = start;
return {
next(): IteratorResult<number> {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined as any, done: true };
},
};
}

const iter = createCountIterator(1, 3);
console.log(iter.next()); // { value: 1, done: false }
console.log(iter.next()); // { value: 2, done: false }
console.log(iter.next()); // { value: 3, done: false }
console.log(iter.next()); // { value: undefined, done: true }
注意

done: true 时的 value 通常为 undefined,但它也可以有值(如 Generator 的 return 返回值)。消费迭代器的语法(如 for...of)会忽略 done: true 时的 value

可迭代协议(Iterable Protocol)

一个对象实现了 [Symbol.iterator]() 方法,返回一个迭代器,就满足可迭代协议。满足可迭代协议的对象可以被 for...of、展开运算符、解构赋值等语法消费。

iterable-protocol.ts
// 可迭代对象接口
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}

// 创建一个可迭代的范围对象
class Range implements Iterable<number> {
constructor(
private start: number,
private end: number,
private step: number = 1
) {}

[Symbol.iterator](): Iterator<number> {
let current = this.start;
const end = this.end;
const step = this.step;

return {
next(): IteratorResult<number> {
if (current <= end) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined as any, done: true };
},
};
}
}

const range = new Range(1, 5);

// for...of 会自动调用 [Symbol.iterator]()
for (const num of range) {
console.log(num); // 1, 2, 3, 4, 5
}

// 展开运算符
console.log([...range]); // [1, 2, 3, 4, 5]

// 解构赋值
const [first, second, ...rest] = range;
console.log(first, second, rest); // 1 2 [3, 4, 5]

// Array.from
console.log(Array.from(range)); // [1, 2, 3, 4, 5]

内置可迭代对象

JavaScript 中以下内置对象默认实现了可迭代协议:

内置对象迭代内容示例
Array数组元素for (const item of [1, 2, 3])
StringUnicode 字符for (const char of 'hello')
Map[key, value] 键值对for (const [k, v] of map)
Set集合元素for (const item of set)
TypedArray类型数组元素for (const byte of uint8Array)
arguments函数参数for (const arg of arguments)
NodeListDOM 节点for (const node of nodeList)
补充

普通对象 {} 默认不是可迭代的,for...of 无法直接遍历普通对象。如果需要遍历对象的键值对,可以使用 Object.entries()Object.keys()Object.values(),它们返回的是数组(数组是可迭代的)。

builtin-iterables.ts
// Map 的迭代
const map = new Map<string, number>([
['a', 1],
['b', 2],
]);
for (const [key, value] of map) {
console.log(`${key}: ${value}`); // a: 1, b: 2
}

// String 支持 Unicode 迭代(正确处理代理对)
const emoji = '👨‍👩‍👧';
console.log([...emoji]); // 正确拆分 emoji

// 普通对象需要通过 Object.entries() 转换
const obj = { x: 1, y: 2 };
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`); // x: 1, y: 2
}

for...of 原理与 for...in 的区别

for...of 的本质是调用可迭代对象的 [Symbol.iterator]() 方法获取迭代器,然后不断调用 next() 直到 donetrue

for-of-desugared.ts
// for...of 语法糖的等价实现
const arr = [10, 20, 30];

// 这段 for...of:
for (const item of arr) {
console.log(item);
}

// 等价于:
const iterator = arr[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
const item = result.value;
console.log(item);
result = iterator.next();
}

for...of vs for...in 对比

特性for...offor...in
遍历内容可迭代对象的对象的可枚举属性键(包括原型链)
适用类型实现了 [Symbol.iterator] 的对象任何对象
数组遍历遍历元素值遍历索引字符串("0", "1", ...)
原型链不涉及会遍历原型链上的可枚举属性
顺序保证按迭代器定义的顺序不保证顺序(实际有规则但规范不强制)
可中断break / return 触发 iterator.return()break 即可
for-of-vs-for-in.ts
const arr = [10, 20, 30];

// for...of 遍历值
for (const value of arr) {
console.log(value); // 10, 20, 30
}

// for...in 遍历键(索引字符串)
for (const key in arr) {
console.log(key); // "0", "1", "2"
}

// ⚠️ for...in 会遍历原型链上的可枚举属性
(Array.prototype as any).customMethod = function () {};
for (const key in arr) {
console.log(key); // "0", "1", "2", "customMethod"
}
警告

永远不要用 for...in 遍历数组!它会遍历原型链属性,且索引是字符串而非数字。遍历数组请使用 for...offorEach 或传统 for 循环。

自定义迭代器实现

链表迭代器

linked-list-iterator.ts
class ListNode<T> {
constructor(
public value: T,
public next: ListNode<T> | null = null
) {}
}

class LinkedList<T> implements Iterable<T> {
head: ListNode<T> | null = null;

append(value: T): void {
const node = new ListNode(value);
if (!this.head) {
this.head = node;
return;
}
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = node;
}

[Symbol.iterator](): Iterator<T> {
let current = this.head;
return {
next(): IteratorResult<T> {
if (current) {
const value = current.value;
current = current.next;
return { value, done: false };
}
return { value: undefined as any, done: true };
},
};
}
}

const list = new LinkedList<number>();
list.append(1);
list.append(2);
list.append(3);

for (const value of list) {
console.log(value); // 1, 2, 3
}
console.log([...list]); // [1, 2, 3]

树形结构迭代器(深度优先)

tree-iterator.ts
interface TreeNode<T> {
value: T;
children: TreeNode<T>[];
}

class Tree<T> implements Iterable<T> {
constructor(private root: TreeNode<T>) {}

// 深度优先遍历(前序)
*[Symbol.iterator](): Generator<T> {
function* traverse(node: TreeNode<T>): Generator<T> {
yield node.value;
for (const child of node.children) {
yield* traverse(child);
}
}
yield* traverse(this.root);
}
}

const tree = new Tree<string>({
value: 'root',
children: [
{
value: 'A',
children: [
{ value: 'A1', children: [] },
{ value: 'A2', children: [] },
],
},
{
value: 'B',
children: [{ value: 'B1', children: [] }],
},
],
});

console.log([...tree]); // ['root', 'A', 'A1', 'A2', 'B', 'B1']

无限序列迭代器

infinite-sequence.ts
// 斐波那契无限序列
function* fibonacci(): Generator<number> {
let a = 0;
let b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}

// 取前 10 个斐波那契数
function take<T>(iterable: Iterable<T>, count: number): T[] {
const result: T[] = [];
for (const item of iterable) {
if (result.length >= count) break;
result.push(item);
}
return result;
}

console.log(take(fibonacci(), 10));
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
要点

无限序列是迭代器模式的独特优势——惰性求值。数据只有在被消费时才会计算,避免了一次性生成所有数据的内存开销。

Generator 函数

Generator 函数是创建迭代器的语法糖,使用 function* 声明,通过 yield 暂停和恢复执行。Generator 函数返回的对象同时满足迭代器协议和可迭代协议。

generator-basics.ts
// 基础 Generator
function* numberGenerator(): Generator<number> {
yield 1;
yield 2;
yield 3;
}

const gen = numberGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

// Generator 同时是可迭代对象
for (const num of numberGenerator()) {
console.log(num); // 1, 2, 3
}

yield* 委托

yield* 可以将迭代委托给另一个可迭代对象或 Generator:

yield-delegation.ts
function* innerGenerator(): Generator<string> {
yield 'inner-1';
yield 'inner-2';
}

function* outerGenerator(): Generator<string | number> {
yield 'start';
yield* innerGenerator(); // 委托给另一个 Generator
yield* [100, 200]; // 委托给数组(也是可迭代对象)
yield 'end';
}

console.log([...outerGenerator()]);
// ['start', 'inner-1', 'inner-2', 100, 200, 'end']

Generator 的双向通信

generator-communication.ts
function* calculator(): Generator<number, string, number> {
const a = yield 0; // 暂停,等待外部传入 a
const b = yield a; // 暂停,等待外部传入 b
return `${a} + ${b} = ${a + b}`;
}

const calc = calculator();
console.log(calc.next()); // { value: 0, done: false }
console.log(calc.next(10)); // { value: 10, done: false } — a = 10
console.log(calc.next(20)); // { value: '10 + 20 = 30', done: true } — b = 20
注意

第一次调用 next() 时传入的参数会被忽略,因为此时没有 yield 表达式在等待接收值。从第二次 next(value) 开始,value 会成为上一个 yield 表达式的返回值。

异步迭代器

ES2018 引入了异步迭代协议,用于处理异步数据流。异步迭代器的 next() 返回 Promise<IteratorResult>

async-iterator.ts
// 异步可迭代接口
interface AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}

interface AsyncIterator<T> {
next(): Promise<IteratorResult<T>>;
}

// 模拟分页 API 请求
async function* fetchPages<T>(
url: string,
maxPages: number = Infinity
): AsyncGenerator<T[]> {
let page = 1;

while (page <= maxPages) {
const response = await fetch(`${url}?page=${page}`);
const data: T[] = await response.json();

if (data.length === 0) break; // 没有更多数据
yield data;
page++;
}
}

// 使用 for await...of 消费异步迭代器
async function loadAllUsers(): Promise<void> {
for await (const users of fetchPages<User>('/api/users', 10)) {
console.log(`加载了 ${users.length} 个用户`);
renderUsers(users);
}
}

流式数据处理

stream-processing.ts
// 异步 Generator 处理 SSE(Server-Sent Events)流
async function* parseSSEStream(
response: Response
): AsyncGenerator<string> {
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';

try {
while (true) {
const { done, value } = await reader.read();
if (done) break;

buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop()!; // 保留未完成的行

for (const line of lines) {
if (line.startsWith('data: ')) {
yield line.slice(6);
}
}
}
} finally {
reader.releaseLock();
}
}

// 消费流式 AI 响应
async function streamChat(prompt: string): Promise<void> {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ prompt }),
});

for await (const chunk of parseSSEStream(response)) {
appendToUI(chunk);
}
}

异步迭代器工具函数

async-iterator-utils.ts
// 异步版 map
async function* asyncMap<T, U>(
source: AsyncIterable<T>,
fn: (item: T) => U | Promise<U>
): AsyncGenerator<U> {
for await (const item of source) {
yield await fn(item);
}
}

// 异步版 filter
async function* asyncFilter<T>(
source: AsyncIterable<T>,
predicate: (item: T) => boolean | Promise<boolean>
): AsyncGenerator<T> {
for await (const item of source) {
if (await predicate(item)) {
yield item;
}
}
}

// 异步版 take
async function* asyncTake<T>(
source: AsyncIterable<T>,
count: number
): AsyncGenerator<T> {
let taken = 0;
for await (const item of source) {
if (taken >= count) break;
yield item;
taken++;
}
}

// 组合使用
async function processStream(): Promise<void> {
const pages = fetchPages<User>('/api/users');
const activeUsers = asyncFilter(pages, (users) =>
users.some((u) => u.isActive)
);
const firstThree = asyncTake(activeUsers, 3);

for await (const users of firstThree) {
console.log(users);
}
}

前端实际应用场景

DOM 树遍历

dom-tree-traversal.ts
// 使用迭代器遍历 DOM 树
function* walkDOM(root: Element): Generator<Element> {
yield root;
for (const child of root.children) {
yield* walkDOM(child); // 递归委托
}
}

// 查找所有包含特定属性的元素
function findElementsByAttribute(
root: Element,
attr: string
): Element[] {
const result: Element[] = [];
for (const el of walkDOM(root)) {
if (el.hasAttribute(attr)) {
result.push(el);
}
}
return result;
}

const elements = findElementsByAttribute(
document.body,
'data-tracking'
);

分页数据懒加载

lazy-pagination.ts
// 分页数据的惰性加载迭代器
class PaginatedData<T> implements AsyncIterable<T> {
constructor(
private fetchFn: (page: number, size: number) => Promise<T[]>,
private pageSize: number = 20
) {}

async *[Symbol.asyncIterator](): AsyncGenerator<T> {
let page = 1;
let hasMore = true;

while (hasMore) {
const items = await this.fetchFn(page, this.pageSize);
for (const item of items) {
yield item; // 逐个 yield,而非整页
}
hasMore = items.length === this.pageSize;
page++;
}
}
}

// 使用
const users = new PaginatedData<User>(
(page, size) => fetch(`/api/users?page=${page}&size=${size}`)
.then((res) => res.json())
);

for await (const user of users) {
renderUserCard(user);
// 可以随时 break 停止加载
if (shouldStop()) break;
}

状态历史遍历(撤销/重做)

history-iterator.ts
class StateHistory<T> implements Iterable<T> {
private history: T[] = [];
private currentIndex: number = -1;

push(state: T): void {
// 如果当前不在最新位置,截断后续状态
this.history = this.history.slice(0, this.currentIndex + 1);
this.history.push(state);
this.currentIndex++;
}

undo(): T | undefined {
if (this.currentIndex > 0) {
return this.history[--this.currentIndex];
}
}

redo(): T | undefined {
if (this.currentIndex < this.history.length - 1) {
return this.history[++this.currentIndex];
}
}

// 正向迭代所有历史状态
[Symbol.iterator](): Iterator<T> {
let index = 0;
const history = this.history;
return {
next(): IteratorResult<T> {
if (index < history.length) {
return { value: history[index++], done: false };
}
return { value: undefined as any, done: true };
},
};
}

// 反向迭代(从最新到最旧)
*reversed(): Generator<T> {
for (let i = this.history.length - 1; i >= 0; i--) {
yield this.history[i];
}
}
}

const history = new StateHistory<string>();
history.push('state-1');
history.push('state-2');
history.push('state-3');

// 正向遍历
for (const state of history) {
console.log(state); // state-1, state-2, state-3
}

// 反向遍历
for (const state of history.reversed()) {
console.log(state); // state-3, state-2, state-1
}

迭代器模式的优缺点

维度说明
优点统一遍历接口,解耦集合与遍历逻辑
支持惰性求值,节省内存
可组合,多个迭代器可串联(map/filter/take)
支持无限序列
缺点单向遍历,无法直接回退(需额外实现)
简单场景下可能过度设计
Generator 的调试体验相对复杂

常见面试问题

Q1: 什么是迭代器协议和可迭代协议?它们之间有什么关系?

答案

迭代器协议(Iterator Protocol):一个对象实现了 next() 方法,该方法返回 { value, done } 形式的结果,就满足迭代器协议。donefalse 表示还有值,为 true 表示迭代结束。

可迭代协议(Iterable Protocol):一个对象实现了 [Symbol.iterator]() 方法,该方法返回一个满足迭代器协议的对象,就满足可迭代协议。

两者的关系:可迭代协议是"工厂",每次调用 [Symbol.iterator]() 创建一个新的迭代器;迭代器协议是"游标",负责实际的遍历逻辑。

// 可迭代对象(实现 Symbol.iterator)
const iterable = {
[Symbol.iterator](): Iterator<number> {
let i = 0;
// 返回迭代器(实现 next)
return {
next(): IteratorResult<number> {
return i < 3
? { value: ++i, done: false }
: { value: undefined as any, done: true };
},
};
},
};

// 每次 for...of 都会创建新的迭代器
for (const n of iterable) console.log(n); // 1, 2, 3
for (const n of iterable) console.log(n); // 1, 2, 3(重新开始)

Q2: for...of 和 for...in 的区别是什么?

答案

对比项for...offor...in
遍历目标(可迭代对象的元素)(对象的可枚举属性名)
适用对象实现了 [Symbol.iterator] 的对象任何对象
原型链不涉及原型链会遍历原型链上的可枚举属性
数组表现遍历元素值遍历索引字符串
普通对象默认不可用(需自定义迭代器)可直接使用
const arr = ['a', 'b', 'c'];

for (const value of arr) console.log(value); // 'a', 'b', 'c'
for (const key in arr) console.log(key); // '0', '1', '2'

// for...in 的陷阱:会遍历原型链
(Array.prototype as any).foo = 'bar';
for (const key in arr) console.log(key); // '0', '1', '2', 'foo' ❌

选择原则:遍历数组/Map/Set 用 for...of,遍历对象属性用 Object.keys() + for...of,尽量避免 for...in

Q3: 如何让一个自定义对象支持 for...of?

答案

实现 [Symbol.iterator]() 方法即可。有两种常见方式:

// 方式一:手写 next() 方法
class TodoList {
private items: string[] = [];

add(item: string): void {
this.items.push(item);
}

[Symbol.iterator](): Iterator<string> {
let index = 0;
const items = this.items;
return {
next(): IteratorResult<string> {
if (index < items.length) {
return { value: items[index++], done: false };
}
return { value: undefined as any, done: true };
},
};
}
}

// 方式二:使用 Generator(更简洁)
class TodoListV2 {
private items: string[] = [];

add(item: string): void {
this.items.push(item);
}

*[Symbol.iterator](): Generator<string> {
for (const item of this.items) {
yield item;
}
// 或简写为: yield* this.items;
}
}

const todos = new TodoListV2();
todos.add('学习迭代器');
todos.add('刷面试题');
for (const todo of todos) {
console.log(todo);
}

推荐使用 Generator 方式,代码更简洁,维护成本更低。

Q4: Generator 和 Iterator 是什么关系?

答案

Generator 是创建迭代器的语法糖。调用 Generator 函数返回的对象同时满足迭代器协议可迭代协议

function* gen(): Generator<number> {
yield 1;
yield 2;
}

const g = gen();

// ✅ 满足迭代器协议(有 next 方法)
console.log(g.next()); // { value: 1, done: false }

// ✅ 满足可迭代协议(有 Symbol.iterator)
console.log(g[Symbol.iterator]() === g); // true(返回自身)

关键区别:

特性手写 IteratorGenerator
语法复杂度需手动维护状态yield 自动管理状态
双向通信不支持next(value) 传值、throw(error) 抛错
嵌套迭代手动递归yield* 委托
return()需手动实现自动支持(执行 finally 块)

Q5: 异步迭代器的应用场景有哪些?

答案

异步迭代器(Symbol.asyncIterator + for await...of)适用于逐步产生的异步数据流场景:

  1. 分页 API 请求:逐页加载数据,每页是一个异步操作
  2. 流式响应:SSE、ReadableStream、AI 流式回复
  3. 文件逐行读取:Node.js 中大文件处理
  4. WebSocket 消息流:将消息封装为异步可迭代
  5. 数据库游标:大量数据的批次读取
// 实际应用:将 WebSocket 包装为异步迭代器
function createWSIterable(url: string): AsyncIterable<string> {
return {
[Symbol.asyncIterator]() {
const ws = new WebSocket(url);
const messageQueue: string[] = [];
let resolve: (() => void) | null = null;
let done = false;

ws.onmessage = (e) => {
messageQueue.push(e.data);
resolve?.();
};
ws.onclose = () => {
done = true;
resolve?.();
};

return {
async next(): Promise<IteratorResult<string>> {
while (messageQueue.length === 0 && !done) {
await new Promise<void>((r) => (resolve = r));
}
if (messageQueue.length > 0) {
return { value: messageQueue.shift()!, done: false };
}
return { value: undefined as any, done: true };
},
async return(): Promise<IteratorResult<string>> {
ws.close();
return { value: undefined as any, done: true };
},
};
},
};
}

// 使用
for await (const message of createWSIterable('wss://example.com')) {
handleMessage(message);
}

Q6: 展开运算符 ... 和解构赋值的底层是如何调用迭代器的?

答案

展开运算符和数组解构赋值都会在底层调用对象的 [Symbol.iterator]() 方法:

function* gen(): Generator<number> {
console.log('yield 1');
yield 1;
console.log('yield 2');
yield 2;
console.log('yield 3');
yield 3;
}

// 展开运算符:消费所有值
const arr = [...gen()];
// 输出: yield 1, yield 2, yield 3
// arr = [1, 2, 3]

// 解构赋值:惰性消费
const [first, second] = gen();
// 输出: yield 1, yield 2(只消费了两个!)
// first = 1, second = 2
要点

解构赋值是惰性的——只消费需要的元素个数。这意味着对无限序列使用解构是安全的:const [a, b] = fibonacci() 只会计算前两个值。

其他底层调用迭代器的语法:

  • Array.from(iterable)
  • new Map(iterable) / new Set(iterable)
  • Promise.all(iterable) / Promise.race(iterable)
  • yield* iterable

Q7: 迭代器的 return() 和 throw() 方法有什么作用?

答案

除了必须的 next() 方法,迭代器还可以实现两个可选方法:

方法触发时机作用
return(value?)breakreturn、异常导致提前退出 for...of释放资源、清理状态
throw(error?)外部向 Generator 内部抛错yield 处触发异常
function* resourceGenerator(): Generator<number> {
console.log('获取资源');
try {
yield 1;
yield 2;
yield 3;
} finally {
console.log('释放资源'); // break 时也会执行
}
}

// break 触发 return(),进而执行 finally
for (const value of resourceGenerator()) {
console.log(value);
if (value === 1) break;
}
// 输出: 获取资源 → 1 → 释放资源

// throw() 的使用
function* errorHandlingGen(): Generator<number> {
try {
yield 1;
yield 2;
} catch (e) {
console.log('捕获错误:', (e as Error).message);
yield -1; // 错误恢复
}
}

const g = errorHandlingGen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.throw(new Error('oops'))); // 捕获错误: oops → { value: -1, done: false }

Q8: 如何实现一个支持链式调用的惰性迭代工具库?

答案

借鉴函数式编程的管道思想,将 mapfiltertake 等操作实现为返回新迭代器的函数,实现惰性链式调用:

lazy-iterator-chain.ts
class LazyIterator<T> implements Iterable<T> {
constructor(private source: Iterable<T>) {}

[Symbol.iterator](): Iterator<T> {
return this.source[Symbol.iterator]();
}

map<U>(fn: (item: T) => U): LazyIterator<U> {
const source = this.source;
return new LazyIterator({
*[Symbol.iterator]() {
for (const item of source) {
yield fn(item);
}
},
});
}

filter(predicate: (item: T) => boolean): LazyIterator<T> {
const source = this.source;
return new LazyIterator({
*[Symbol.iterator]() {
for (const item of source) {
if (predicate(item)) yield item;
}
},
});
}

take(count: number): LazyIterator<T> {
const source = this.source;
return new LazyIterator({
*[Symbol.iterator]() {
let taken = 0;
for (const item of source) {
if (taken >= count) break;
yield item;
taken++;
}
},
});
}

// 终结操作:收集为数组
toArray(): T[] {
return [...this];
}

// 终结操作:归约
reduce<U>(fn: (acc: U, item: T) => U, initial: U): U {
let acc = initial;
for (const item of this) {
acc = fn(acc, item);
}
return acc;
}
}

// 辅助函数
function lazy<T>(source: Iterable<T>): LazyIterator<T> {
return new LazyIterator(source);
}

// 使用示例 —— 所有操作都是惰性的,只有 toArray() 时才真正执行
const result = lazy([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
.filter((n) => n % 2 === 0) // 惰性:不会立即执行
.map((n) => n * n) // 惰性
.take(3) // 惰性
.toArray(); // 终结操作,触发计算

console.log(result); // [4, 16, 36]
要点

这种惰性链式迭代模式在处理大数据集时非常高效——take(3) 意味着最多只会遍历到第 6 个元素(3 个偶数),而不是遍历整个数组。这就是惰性求值相比急切求值(如 Array.prototype.filter + map)的核心优势。

Q9: 迭代器模式在 React/Vue 等前端框架中有哪些实际应用?

答案

  1. React 中 children 的迭代React.Children 提供了遍历子元素的迭代工具

  2. 状态管理中的时间旅行:Redux DevTools 的撤销/重做本质上是状态历史的迭代

  3. 虚拟列表数据源:将大数据集包装为惰性迭代器,按需渲染

  4. 路由匹配:React Router 内部使用迭代器模式匹配路由配置

  5. 异步数据加载:使用异步迭代器实现数据流

// 实际案例:React 中用异步迭代器实现无限滚动 Hook
function useAsyncIterator<T>(
iteratorFactory: () => AsyncIterableIterator<T>
): { items: T[]; loadMore: () => void; loading: boolean } {
const [items, setItems] = useState<T[]>([]);
const [loading, setLoading] = useState(false);
const iteratorRef = useRef<AsyncIterableIterator<T>>();

useEffect(() => {
iteratorRef.current = iteratorFactory();
}, []);

const loadMore = useCallback(async () => {
if (!iteratorRef.current || loading) return;
setLoading(true);

const { value, done } = await iteratorRef.current.next();
if (!done && value !== undefined) {
setItems((prev) => [...prev, value]);
}

setLoading(false);
}, [loading]);

return { items, loadMore, loading };
}

Q10: Iterator 与 Array 的高阶方法(map/filter/reduce)有什么区别?如何选择?

答案

对比项Array 高阶方法Iterator
求值策略急切求值:立即处理所有元素惰性求值:按需计算
内存占用每步生成新数组不生成中间数组
无限序列不支持支持
链式操作每步都完整遍历单次遍历完成所有操作
可读性更直观需要理解迭代器概念
浏览器支持全支持需 ES6+(Generator 需 ES2015+)

选择原则

  • 数据量小、操作简单 → Array 方法(可读性优先)
  • 数据量大、多步操作 → Iterator(减少中间数组的内存开销)
  • 无限序列、流式数据 → Iterator(唯一选择)
  • 异步数据流 → 异步迭代器
// 数组方法:三次遍历,创建两个中间数组
const result1 = [1, 2, 3, 4, 5]
.filter((n) => n % 2 === 0) // 遍历 5 个,生成 [2, 4]
.map((n) => n * 10) // 遍历 2 个,生成 [20, 40]
.slice(0, 1); // [20]

// Iterator:单次遍历,零中间数组
const result2 = lazy([1, 2, 3, 4, 5])
.filter((n) => n % 2 === 0)
.map((n) => n * 10)
.take(1)
.toArray(); // 只遍历到元素 2 就停了!结果 [20]
补充

TC39 提案 Iterator Helpers 正在推进中,未来 JavaScript 内置迭代器将原生支持 .map().filter().take() 等方法,届时惰性迭代将成为一等公民。

相关链接