第12章:迭代器与生成器(Iterators and Generators)
一、导读
1.1 本章学习目标
第12章介绍ES6的迭代器和生成器。本章核心目标:
- 理解迭代器协议:next()方法
- 掌握可迭代对象:Symbol.iterator
- 学会使用生成器函数:function*和yield
- 理解生成器的惰性求值
- 掌握yield*委托
1.2 为什么本章如此重要
迭代器和生成器是JavaScript的高级控制流机制。生成器可以暂停和恢复执行,实现协程、无限序列、惰性求值等强大功能。理解它们是掌握JavaScript异步编程的基础。
1.3 核心问题引导
迭代器协议是什么?for...of是如何工作的?
生成器和普通函数有什么本质区别?
yield*委托的执行流程是什么?
生成器如何实现惰性求值?
二、核心概念详解
12.1 迭代器协议
javascript
// 自定义迭代器
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
return {
next: () => {
if (current <= this.to) {
return {value: current++, done: false};
} else {
return {done: true};
}
}
};
}
};
for (let num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
// 手动使用迭代器
let iterator = range[Symbol.iterator]();
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
// 可迭代对象的应用
let arr = [1, 2, 3];
let str = "hello";
let set = new Set([1, 2, 3]);
let map = new Map([["a", 1], ["b", 2]]);
// 都可以用for...of遍历
for (let item of arr) console.log(item);
for (let char of str) console.log(char);
for (let item of set) console.log(item);
for (let [key, value] of map) console.log(key, value);
// 展开运算符也使用迭代器
console.log([...range]); // [1, 2, 3, 4, 5]12.2 生成器函数
javascript
// 生成器函数
function* fibonacci() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
let fib = fibonacci();
for (let i = 0; i < 10; i++) {
console.log(fib.next().value);
}
// 有限生成器
function* countDown(start) {
while (start > 0) {
console.log(`Counting: ${start}`);
yield start--;
}
console.log("Done!");
}
let counter = countDown(5);
counter.next(); // Counting: 5, {value: 5, done: false}
counter.next(); // Counting: 4, {value: 4, done: false}
// 生成器作为迭代器
function* range(start, end) {
for (let i = start; i <= end; i++) {
yield i;
}
}
console.log([...range(1, 5)]); // [1, 2, 3, 4, 5]12.3 yield*委托
javascript
function* gen1() {
yield 1;
yield 2;
}
function* gen2() {
yield* gen1(); // 委托给另一个生成器
yield 3;
yield 4;
}
for (let value of gen2()) {
console.log(value); // 1, 2, 3, 4
}
// yield*与字符串
function* chars(str) {
yield* str;
}
console.log([...chars("hello")]); // ["h", "e", "l", "l", "o"]
// 递归生成器
function* tree(node) {
if (node.value) yield node.value;
if (node.left) yield* tree(node.left);
if (node.right) yield* tree(node.right);
}
let treeNode = {
value: 1,
left: {value: 2, left: {value: 4}, right: {value: 5}},
right: {value: 3}
};
console.log([...tree(treeNode)]); // [1, 2, 4, 5, 3]12.4 生成器的控制
javascript
// 向生成器传值
function* calculator() {
let result = 0;
while (true) {
let operation = yield result;
switch (operation) {
case 'add': result += yield; break;
case 'subtract': result -= yield; break;
case 'multiply': result *= yield; break;
case 'reset': result = 0; break;
}
}
}
let calc = calculator();
console.log(calc.next().value); // 0
console.log(calc.next('add').value); // 0
console.log(calc.next(5).value); // 5
console.log(calc.next('multiply').value); // 5
console.log(calc.next(3).value); // 15
// 生成器的return
function* gen() {
yield 1;
yield 2;
return 3; // return的值不会在for...of中出现
}
let g = gen();
console.log(g.next()); // {value: 1, done: false}
console.log(g.next()); // {value: 2, done: false}
console.log(g.next()); // {value: 3, done: true}
// 生成器的throw
function* errorHandler() {
try {
yield 1;
yield 2;
} catch (e) {
console.log("Error caught:", e);
yield "error handled";
}
}
let eh = errorHandler();
console.log(eh.next().value); // 1
console.log(eh.throw("oops")); // Error caught: oops, "error handled"三、实战案例
3.1 异步生成器
javascript
// 异步生成器
async function* fetchPages(url) {
let page = 1;
while (true) {
const response = await fetch(`${url}?page=${page}`);
const data = await response.json();
if (data.length === 0) break;
yield data;
page++;
}
}
// 使用异步迭代器
async function loadAllData() {
for await (const page of fetchPages('/api/items')) {
console.log(`Loaded page with ${page.length} items`);
processItems(page);
}
}
// 异步生成器实现轮询
async function* poll(interval, fn) {
while (true) {
const result = await fn();
yield result;
await new Promise(resolve => setTimeout(resolve, interval));
}
}
// 使用
async function monitor() {
for await (const status of poll(1000, checkServerStatus)) {
console.log(`Server status: ${status}`);
if (status === 'down') break;
}
}四、本章小结
第12章介绍了迭代器和生成器:迭代器协议(next()方法)、可迭代对象(Symbol.iterator)、生成器函数(function/yield)、yield委托、异步生成器。生成器提供了强大的控制流能力。
五、思考与练习
思考题
for...of和for...in有什么区别?
生成器的暂停和恢复是如何实现的?
异步生成器和Promise有什么本质区别?
生成器可以替代回调函数吗?
编程练习
实现一个无限序列生成器(如素数序列)。
使用生成器实现一个简单的协程。
实现一个异步数据流管道。