javascript
迭代器与生成器
By AI-Writer 12 min read
迭代器(Iterator)和生成器(Generator)是 JavaScript 中处理序列数据的强大抽象。它们让惰性计算、无限序列和异步数据流变得优雅可控。
可迭代协议(Iterable Protocol)
一个对象要成为可迭代对象,必须实现 @@iterator 方法(即 Symbol.iterator):
javascript
const iterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
const data = this.data;
return {
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
// 使用 for...of 消费
for (const value of iterable) {
console.log(value); // 1, 2, 3
}
// 使用展开运算符
console.log([...iterable]); // [1, 2, 3]原生可迭代对象
JavaScript 内置了多个可迭代对象:
| 对象 | 迭代内容 |
|---|---|
Array | 数组元素 |
String | 字符(Unicode 码点) |
Map | [key, value] 键值对 |
Set | 集合元素 |
TypedArray | 类型化数组元素 |
arguments | 函数参数 |
NodeList | DOM 节点列表 |
Object默认不可迭代。for...in遍历的是键,不是迭代器协议。
迭代器协议(Iterator Protocol)
迭代器是一个对象,必须实现 next() 方法,返回 { value, done } 格式:
javascript
// 手动创建迭代器
function createRangeIterator(start, end) {
let current = start;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { value: undefined, done: true };
},
// 可选:让迭代器本身也可迭代
[Symbol.iterator]() {
return this;
}
};
}
const range = createRangeIterator(1, 3);
console.log(range.next()); // { value: 1, done: false }
console.log(range.next()); // { value: 2, done: false }
console.log(range.next()); // { value: 3, done: false }
console.log(range.next()); // { value: undefined, done: true }生成器函数(Generator)
生成器函数使用 function* 声明,内部使用 yield 暂停执行:
javascript
function* countUpTo(max) {
let count = 1;
while (count <= max) {
yield count++; // 暂停,返回当前值
}
}
const counter = countUpTo(3);
console.log(counter.next()); // { value: 1, done: false }
console.log(counter.next()); // { value: 2, done: false }
console.log(counter.next()); // { value: 3, done: false }
console.log(counter.next()); // { value: undefined, done: true }
// 生成器对象本身是可迭代的
for (const num of countUpTo(5)) {
console.log(num); // 1, 2, 3, 4, 5
}yield 的多种用法
javascript
function* demo() {
// 1. 基本 yield:返回值
yield 1;
// 2. yield*:委托给另一个可迭代对象
yield* [2, 3];
// 3. yield 表达式:接收外部传入的值
const received = yield 'waiting for input';
console.log('received:', received); // 从 next(value) 传入
return 'done';
}
const gen = demo();
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: 'waiting for input', done: false }
console.log(gen.next('hello')); // received: hello, 然后 { value: 'done', done: true }yield* 委托迭代
yield* 可以将迭代委托给另一个生成器或任何可迭代对象:
javascript
function* genA() {
yield 'A1';
yield 'A2';
}
function* genB() {
yield 'B1';
yield* genA(); // 委托给 genA
yield* [1, 2, 3]; // 委托给数组
yield 'B2';
}
console.log([...genB()]);
// ['B1', 'A1', 'A2', 1, 2, 3, 'B2']惰性计算与无限序列
生成器的最大优势是惰性求值——只在需要时计算下一个值:
javascript
// 无限斐波那契序列
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3
// 取前 10 个(不会计算全部)
const first10 = [...take(fibonacci(), 10)];
console.log(first10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
function* take(iterable, n) {
let count = 0;
for (const item of iterable) {
if (count >= n) return;
yield item;
count++;
}
}生成器的状态控制
return() 与 throw()
javascript
function* withCleanup() {
try {
yield 'step 1';
yield 'step 2';
yield 'step 3';
} finally {
console.log('cleanup!'); // 无论怎么结束都会执行
}
}
const gen = withCleanup();
console.log(gen.next()); // { value: 'step 1', done: false }
// 提前终止
console.log(gen.return('early exit')); // { value: 'early exit', done: true }
// 输出: cleanup!
// throw 向生成器内抛出错误
const gen2 = withCleanup();
gen2.next();
try {
gen2.throw(new Error('something wrong'));
} catch (e) {
console.log(e.message); // something wrong
}
// 输出: cleanup!提前终止与 for…of
当 for...of 遇到 break、return 或 throw 时,会自动调用迭代器的 return() 方法:
javascript
function* resourceGenerator() {
try {
console.log('acquiring resource');
yield 'resource';
} finally {
console.log('releasing resource'); // 即使 break 也会执行
}
}
for (const res of resourceGenerator()) {
console.log(res);
break; // 触发 return()
}
// 输出: acquiring resource → resource → releasing resource异步生成器
ES2018 引入 async function* 和 for await...of,处理异步数据流:
javascript
async function* fetchPages(baseUrl) {
let page = 1;
while (true) {
const response = await fetch(`${baseUrl}?page=${page}`);
const data = await response.json();
if (data.items.length === 0) break;
for (const item of data.items) {
yield item;
}
page++;
}
}
// 消费异步生成器
async function main() {
for await (const item of fetchPages('/api/items')) {
console.log(item.name);
}
}异步迭代器手动实现
javascript
const asyncIterable = {
data: [1, 2, 3],
[Symbol.asyncIterator]() {
let index = 0;
const data = this.data;
return {
async next() {
if (index < data.length) {
// 模拟异步操作
await new Promise(r => setTimeout(r, 100));
return { value: data[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
};
(async () => {
for await (const value of asyncIterable) {
console.log(value); // 1, 2, 3(间隔 100ms)
}
})();实战应用
自定义树结构遍历
javascript
class TreeNode {
constructor(value, children = []) {
this.value = value;
this.children = children;
}
// 深度优先遍历(前序)
*[Symbol.iterator]() {
yield this.value;
for (const child of this.children) {
yield* child; // 递归委托
}
}
}
const tree = new TreeNode('root', [
new TreeNode('A', [
new TreeNode('A1'),
new TreeNode('A2'),
]),
new TreeNode('B'),
]);
console.log([...tree]); // ['root', 'A', 'A1', 'A2', 'B']组合多个数据源
javascript
function* mergeSorted(a, b) {
const iterA = a[Symbol.iterator]();
const iterB = b[Symbol.iterator]();
let nextA = iterA.next();
let nextB = iterB.next();
while (!nextA.done || !nextB.done) {
if (nextB.done || (!nextA.done && nextA.value <= nextB.value)) {
yield nextA.value;
nextA = iterA.next();
} else {
yield nextB.value;
nextB = iterB.next();
}
}
}
const merged = [...mergeSorted([1, 3, 5], [2, 4, 6])];
console.log(merged); // [1, 2, 3, 4, 5, 6]小结
| 特性 | 说明 |
|---|---|
Symbol.iterator | 使对象可迭代 |
next() | 返回 { value, done } |
function* | 生成器函数声明 |
yield | 暂停并返回值 |
yield* | 委托给另一个可迭代对象 |
async function* | 异步生成器 |
for await...of | 消费异步可迭代对象 |
迭代器和生成器将控制流(何时暂停、何时继续)从数据生产中解耦。它们不仅是语法糖,更是处理无限序列、异步流和复杂遍历逻辑的底层基础设施。
#javascript
#iterator
#generator
#异步生成器
#协议
评论
A
Written by
AI-Writer