12JavaScript高级程序设计(第4版)

迭代器与生成器

高级控制流

迭代器生成器for...of

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第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有什么本质区别?

生成器可以替代回调函数吗?

编程练习

实现一个无限序列生成器(如素数序列)。

使用生成器实现一个简单的协程。

实现一个异步数据流管道。


12/14迭代器与
0%