{T}

迭代器与生成器

迭代器(Iterator)和生成器(Generator)是 ES6 引入的核心特性,提供了一种统一的数据遍历机制和流程控制能力。

概述

核心概念

  • 迭代器(Iterator):一种协议,定义了统一的遍历集合的方式,提供 next() 方法按需获取下一个值
  • 生成器(Generator):一种特殊的函数,可以暂停执行并逐步产出值,简化迭代器的创建
  • 可迭代对象(Iterable):实现了 [Symbol.iterator] 方法的对象,可以被 for...of 等语法遍历

工作流程图

code
┌─────────────────────────────────────────────────────────┐
│                    迭代器工作流程                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  可迭代对象(Object)                                      │
│       │                                                 │
│       │ [Symbol.iterator]()                             │
│       ▼                                                 │
│  迭代器(Iterator)                                        │
│       │                                                 │
│       │ next()                                          │
│       ▼                                                 │
│  { value: any, done: boolean }                          │
│       │                                                 │
│       ├─ done: false → 继续调用 next()                   │
│       └─ done: true  → 迭代结束                          │
│                                                         │
└─────────────────────────────────────────────────────────┘

一、迭代器(Iterator)

1.1 迭代协议详解

ES6 定义了两层协议:

1.1.1 可迭代协议(Iterable Protocol)

对象实现 [Symbol.iterator]() 方法,该方法返回一个迭代器对象:

javascript
const iterable = {
  [Symbol.iterator]() {
    return {
      current: 0,
      last: 5,
      next() {
        if (this.current <= this.last) {
          return { value: this.current++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

// 验证是否可迭代
console.log(typeof iterable[Symbol.iterator]); // 'function'

1.1.2 迭代器协议(Iterator Protocol)

迭代器对象必须实现 next() 方法,返回包含 valuedone 的对象:

属性类型说明
valueany当前迭代的值,迭代结束时可为 undefined
doneboolean是否已迭代完毕

协议要求:

  • next() 方法必须返回对象 { value, done }
  • donetrue 时表示迭代结束
  • 迭代结束后可继续调用 next(),应始终返回 { done: true }

1.2 手动实现迭代器

javascript
// 简单迭代器对象
const iterator = {
  current: 0,
  last: 5,
  next() {
    if (this.current <= this.last) {
      return { value: this.current++, done: false };
    }
    return { value: undefined, done: true };
  },
};

// 使用迭代器
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }

1.3 创建可迭代对象

让对象可迭代,需要实现 [Symbol.iterator]() 方法:

javascript
// 示例1: 创建数字范围迭代器
const range = {
  start: 1,
  end: 5,
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        }

  // ... 中间省略 ...

// 使用解构赋值
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
// 示例2: 创建自定义集合类
class MyCollection {
  constructor(...items) {
    this.items = items;
  }
  
  [Symbol.iterator]() {
    let index = 0;
    const items = this.items;
    
    return {
      next() {

  // ... 中间省略 ...

}

const collection = new MyCollection(1, 2, 3, 4, 5);
for (const item of collection) {
  console.log(item); // 1, 2, 3, 4, 5
}

1.4 内置可迭代对象

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

对象类型说明迭代内容
Array数组元素值
String字符串字符编码单元
Map映射表[key, value] 键值对
Set集合元素值
arguments函数参数参数值
NodeListDOM 节点列表DOM 节点
TypedArray类型化数组元素值
Int8Array具体类型数组元素值

内置对象迭代示例:

javascript
// 1. 数组迭代
const arr = [1, 2, 3];
for (const item of arr) {
  console.log(item); // 1, 2, 3
}

// 数组迭代器
const arrIterator = arr[Symbol.iterator]();
console.log(arrIterator.next()); // { value: 1, done: false }
console.log(arrIterator.next()); // { value: 2, done: false }

// 2. 字符串迭代

  // ... 中间省略 ...


// 6. NodeList 迭代
const divs = document.querySelectorAll('div');
for (const div of divs) {
  console.log(div);
}

1.5 迭代器的可选方法

完整的迭代器还可以实现两个可选方法:

return(value)

当迭代提前终止时调用(如使用 breakreturn 或抛出异常):

javascript
const iterable = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        if (i < 5) {
          return { value: i++, done: false };
        }
        return { done: true };
      },
      return(value) {
        console.log('清理资源');
        return { value, done: true };
      }
    };
  }
};

// 提前终止会调用 return
for (const item of iterable) {
  if (item === 2) break;
  console.log(item); // 0, 1
}
// 输出: 清理资源

throw(error)

主要用于生成器,向生成器抛出错误(稍后在生成器部分详细说明)。


二、生成器(Generator)

2.1 基本语法

生成器使用 function* 声明,内部使用 yield 产出值:

javascript
// 基本生成器
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator();

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 }

语法要点:

  • 函数声明时在 function 后添加 *
  • 可以是 function* name()function *name() (推荐前者)
  • 内部使用 yield 表达式产出值
  • 调用生成器函数返回一个生成器对象(也是迭代器)

2.2 yield 表达式详解

yield 表达式具有双向通信能力:

2.2.1 产出值

javascript
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator();
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 }

2.2.2 接收值

javascript
function* generator() {
  console.log('开始');
  const a = yield 1;       // yield 表达式的返回值
  console.log('a:', a);
  const b = yield 2;
  console.log('b:', b);
  return 3;
}

const gen = generator();

// 第一次调用 next(),启动生成器,执行到第一个 yield
console.log(gen.next());
// 输出: 开始
// 返回: { value: 1, done: false }

// 第二次调用 next('A'),从上次暂停处继续,将 'A' 作为 yield 表达式的返回值
console.log(gen.next('A'));
// 输出: a: A
// 返回: { value: 2, done: false }

// 第三次调用
console.log(gen.next('B'));
// 输出: b: B
// 返回: { value: 3, done: true }

执行流程图:

code
┌─────────────────────────────────────────────────────────┐
│              生成器执行流程                               │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  gen.next()         → 启动生成器,执行到 yield 1          │
│                         ↓                               │
│                     { value: 1, done: false }           │
│                         ↓                               │
│  gen.next('A')      → 恢复执行,'A' → a,执行到 yield 2    │
│                         ↓                               │
│                     { value: 2, done: false }           │
│                         ↓                               │
│  gen.next('B')      → 恢复执行,'B' → b,执行到 return 3   │
│                         ↓                               │
│                     { value: 3, done: true }            │
│                                                         │
└─────────────────────────────────────────────────────────┘

2.2.3 yield 表达式的值

javascript
function* generator() {
  // yield 表达式本身的值是 undefined(如果没有通过 next() 传值)
  let result = yield 'first';
  console.log('接收到的值:', result); // 接收到的值: undefined
  
  // 可以通过 next(value) 传递值
  result = yield 'second';
  console.log('接收到的值:', result); // 接收到的值: hello
}

const gen = generator();
gen.next();              // { value: 'first', done: false }
gen.next();              // 接收到的值: undefined, { value: 'second', done: false }
gen.next('hello');       // 接收到的值: hello, { value: undefined, done: true }

2.3 yield* 委托表达式

yield* 用于委托给另一个生成器或可迭代对象:

javascript
function* generator1() {
  yield 1;
  yield 2;
}

function* generator2() {
  yield 'a';
  yield* generator1();  // 委托给 generator1
  yield* [3, 4];        // 委托给数组
  yield 'b';
}

for (const value of generator2()) {
  console.log(value);
}
// 输出: 'a', 1, 2, 3, 4, 'b'

yield 的特点:*

  • 委托给任何可迭代对象(数组、字符串、Set、Map、其他生成器等)
  • 会完全消耗被委托的迭代器
  • yield* 表达式的值是被委托迭代器的返回值
javascript
function* generator1() {
  yield 1;
  yield 2;
  return 'done';  // 生成器的返回值
}

function* generator2() {
  const result = yield* generator1();  // result = 'done'
  console.log('委托结果:', result);
  yield 3;
}

const gen = generator2();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // 委托结果: done, { value: 3, done: false }

应用场景:

javascript
// 嵌套结构扁平化
function* flattenArray(arr) {
  for (const item of arr) {
    if (Array.isArray(item)) {
      yield* flattenArray(item);  // 递归委托
    } else {
      yield item;
    }
  }
}

const nested = [1, [2, 3], [4, [5, 6]]];
console.log([...flattenArray(nested)]); // [1, 2, 3, 4, 5, 6]

2.4 生成器方法

生成器对象有三个方法:

方法说明返回值
next(value)恢复执行{ value, done }
return(value)终止生成器{ value, done: true }
throw(error)抛出错误{ value, done }

2.4.1 next(value)

恢复生成器执行,可选传入值:

javascript
function* generator() {
  const input = yield;
  console.log('Received:', input);
}

const gen = generator();
gen.next();          // 启动生成器,执行到第一个 yield
gen.next('Hello');   // Received: Hello

注意事项:

  • 第一次调用 next() 时传递的值会被忽略
  • 因为第一次调用只是启动生成器,执行到第一个 yield 处暂停
  • 从第二次调用开始,传入的值会作为上一个 yield 表达式的返回值
javascript
function* generator() {
  console.log('Start');
  const a = yield 1;
  console.log('a =', a);
  const b = yield 2;
  console.log('b =', b);
}

const gen = generator();

// ❌ 错误示范:第一次调用传入的值被忽略
gen.next('ignored');   // Start, { value: 1, done: false }
gen.next('A');         // a = A, { value: 2, done: false }
gen.next('B');         // b = B, { value: undefined, done: true }

// ✅ 正确示范:从第二次开始传值
const gen2 = generator();
gen2.next();           // Start, { value: 1, done: false }
gen2.next('A');        // a = A, { value: 2, done: false }
gen2.next('B');        // b = B, { value: undefined, done: true }

2.4.2 return(value)

立即终止生成器并返回值:

javascript
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator();
console.log(gen.next());           // { value: 1, done: false }
console.log(gen.return('done'));   // { value: 'done', done: true }
console.log(gen.next());           // { value: undefined, done: true }

触发 return 的场景:

javascript
function* generator() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } finally {
    console.log('清理资源');  // finally 块总是执行
  }
}

// 场景1: 显式调用 return()
const gen1 = generator();

  // ... 中间省略 ...

  const gen = generator();
  for (const value of gen) {
    if (value === 2) return value;  // 触发 return()
  }
}
console.log(findValue());  // 1, 清理资源, 2

注意:

  • 如果生成器有 finally 块,return() 会先执行 finally
  • finally 块执行后,生成器进入完成状态
  • 后续的 next() 调用总是返回 { value: undefined, done: true }

2.4.3 throw(error)

向生成器内部抛出错误:

javascript
function* generator() {
  try {
    yield 1;
    yield 2;
  } catch (error) {
    console.log('Caught:', error.message);
  }
  yield 3;
}

const gen = generator();
console.log(gen.next());                      // { value: 1, done: false }
console.log(gen.throw(new Error('Error!'))); // Caught: Error!, { value: 3, done: false }
console.log(gen.next());                      // { value: undefined, done: true }

错误处理流程:

javascript
function* generator() {
  try {
    console.log('Start');
    yield 1;
    console.log('After yield 1');
    yield 2;
  } catch (error) {
    console.log('Caught error:', error.message);
    yield 'recovered';
  } finally {
    console.log('Finally');
  }
  yield 3;
}

const gen = generator();
console.log(gen.next());                        // Start, { value: 1, done: false }

// 抛出错误:错误会被当前的 try-catch 捕获
console.log(gen.throw(new Error('Oops!')));
// 输出: Caught error: Oops!
// 返回: { value: 'recovered', done: false }

console.log(gen.next());  // Finally, { value: 3, done: false }
console.log(gen.next());  // { value: undefined, done: true }

如果错误未被捕获:

javascript
function* generator() {
  yield 1;
  yield 2;  // 没有 try-catch
}

const gen = generator();
gen.next();  // { value: 1, done: false }

try {
  gen.throw(new Error('Not caught'));
} catch (error) {
  console.log('外部捕获:', error.message);  // 外部捕获: Not caught
}

// 生成器已关闭
console.log(gen.next());  // { value: undefined, done: true }

2.5 生成器的特殊形式

2.5.1 对象方法

javascript
const obj = {
  // 简写形式
  *generator1() {
    yield 1;
    yield 2;
  },
  
  // 完整形式
  generator2: function*() {
    yield 3;
    yield 4;
  }
};

console.log([...obj.generator1()]); // [1, 2]
console.log([...obj.generator2()]); // [3, 4]

2.5.2 类方法

javascript
class MyClass {
  // 实例方法
  *generator() {
    yield this.value;
  }
  
  // 静态方法
  static *staticGenerator() {
    yield 'static';
  }
}

const instance = new MyClass();
instance.value = 42;
console.log([...instance.generator()]);  // [42]
console.log([...MyClass.staticGenerator()]); // ['static']

2.5.3 匿名生成器

javascript
// 函数表达式
const gen1 = function*() {
  yield 1;
};

// 箭头函数 ❌ 不支持
// const gen2 = *() => { yield 1; };  // SyntaxError

// 作为参数
function* processItems(items, *transform(item)) {
  for (const item of items) {
    yield* transform(item);
  }
}

三、异步迭代器与异步生成器

ES2018 引入了异步迭代器和异步生成器,用于处理异步数据流。

3.1 异步迭代协议

异步迭代使用 [Symbol.asyncIterator] 方法:

javascript
const asyncIterable = {
  [Symbol.asyncIterator]() {
    let i = 0;
    return {
      next() {
        if (i < 3) {
          return Promise.resolve({ value: i++, done: false });
        }
        return Promise.resolve({ done: true });
      }
    };
  }
};

// 使用 for await...of 遍历
(async () => {
  for await (const value of asyncIterable) {
    console.log(value);  // 0, 1, 2
  }
})();

3.2 异步生成器

使用 async function* 声明异步生成器:

javascript
// 异步生成器
async function* asyncGenerator() {
  const data = await fetch('/api/data');
  const json = await data.json();
  
  for (const item of json) {
    yield item;
  }
}

// 使用 for await...of
(async () => {
  for await (const item of asyncGenerator()) {
    console.log(item);
  }
})();

3.3 实际应用示例

示例1: 分页数据异步迭代

javascript
async function* fetchPages(url) {
  let page = 1;
  let hasMore = true;
  
  while (hasMore) {
    const response = await fetch(`${url}?page=${page}`);
    const data = await response.json();
    
    yield data.items;
    
    hasMore = data.hasMore;
    page++;
  }
}

// 使用
(async () => {
  for await (const items of fetchPages('/api/users')) {
    for (const item of items) {
      console.log(item);
    }
  }
})();

示例2: 文件行读取

javascript
async function* readLines(filePath) {
  const fileHandle = await fs.promises.open(filePath, 'r');
  const stream = fileHandle.createReadStream();
  const reader = readline.createInterface({
    input: stream,
    crlfDelay: Infinity
  });
  
  for await (const line of reader) {
    yield line;
  }
  
  await fileHandle.close();
}

// 使用
(async () => {
  for await (const line of readLines('./data.txt')) {
    console.log(line);
  }
})();

示例3: 实时数据流

javascript
async function* eventStream(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    const chunk = decoder.decode(value, { stream: true });
    yield chunk;
  }
}

// 使用
(async () => {
  for await (const chunk of eventStream('/api/stream')) {
    processChunk(chunk);
  }
})();

3.4 异步迭代器 vs 同步迭代器

特性同步迭代器异步迭代器
Symbol[Symbol.iterator][Symbol.asyncIterator]
next() 返回值{ value, done }Promise<{ value, done }>
遍历语法for...offor await...of
使用场景内存中的数据集合异步数据流、网络请求
性能特点立即返回每次迭代都可能是异步操作

3.5 内置异步可迭代对象

ReadableStream

javascript
// 浏览器环境
const response = await fetch('/api/data');
const reader = response.body.getReader();

// ReadableStream 实现了异步迭代
for await (const chunk of response.body) {
  console.log(chunk);
}

Node.js 可读流

javascript
// Node.js 环境
const fs = require('fs');

async function processFile() {
  const stream = fs.createReadStream('./data.txt', { encoding: 'utf8' });
  
  for await (const chunk of stream) {
    console.log(chunk);
  }
}

四、生成器实现迭代器

生成器是创建迭代器的简便方法:

4.1 传统方式 vs 生成器方式

javascript
// ❌ 传统迭代器:冗长且易错
const range1 = {
  start: 1,
  end: 5,
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        }

  // ... 中间省略 ...

  },
};

for (const num of range2) {
  console.log(num); // 1, 2, 3, 4, 5
}

4.2 带返回值的迭代器

javascript
const range = {
  start: 1,
  end: 5,
  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
    return '完成';  // 迭代器返回值
  }
};

const iterator = range[Symbol.iterator]();
let result;
while (!(result = iterator.next()).done) {
  console.log(result.value);  // 1, 2, 3, 4, 5
}
console.log(result.value);  // '完成'

4.3 支持提前终止的迭代器

javascript
const range = {
  start: 1,
  end: 5,
  *[Symbol.iterator]() {
    try {
      for (let i = this.start; i <= this.end; i++) {
        yield i;
      }
    } finally {
      console.log('清理资源');
    }
  }
};

// 提前终止会执行 finally
for (const num of range) {
  console.log(num);  // 1, 2
  if (num === 2) break;
}
// 输出: 清理资源

五、实际应用场景

5.1 无限序列

javascript
// 无限数字序列
function* infiniteSequence() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

const gen = infiniteSequence();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2

  // ... 中间省略 ...

  }
}

const primeGen = primes();
console.log([...Array(10)].map(() => primeGen.next().value));
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

5.2 懒加载与按需计算

javascript
// 懒加载示例
function* lazyRange(start, end) {
  console.log('Starting...');
  for (let i = start; i <= end; i++) {
    console.log(`Generating ${i}`);
    yield i;
  }
}

const range = lazyRange(1, 5);
console.log('Created');
console.log(range.next().value); // Starting... Generating 1, 1

  // ... 中间省略 ...

for (const line of readLines(content)) {
  console.log(line);
}
// 第一行
// 第二行
// 第三行

5.3 异步流程控制(旧方案)

javascript
// 使用生成器进行异步流程控制(ES6 时代的方案)
function* asyncTask() {
  try {
    const user = yield fetchUser();
    const posts = yield fetchPosts(user.id);
    const comments = yield fetchComments(posts[0].id);
    return comments;
  } catch (error) {
    console.error('Error:', error);
  }
}


  // ... 中间省略 ...

    const comments = await fetchComments(posts[0].id);
    return comments;
  } catch (error) {
    console.error('Error:', error);
  }
}

5.4 树结构遍历

javascript
// 二叉树节点
class TreeNode {
  constructor(value, left = null, right = null) {
    this.value = value;
    this.left = left;
    this.right = right;
  }

  // 中序遍历
  *[Symbol.iterator]() {
    if (this.left) yield* this.left;
    yield this.value;

  // ... 中间省略 ...

}

// 使用
for (const node of walkDOM(document.body)) {
  console.log(node.tagName);
}

5.5 大数据分块处理

javascript
// 数组分块
function* chunk(array, size) {
  for (let i = 0; i < array.length; i += size) {
    yield array.slice(i, i + size);
  }
}

const largeArray = Array.from({ length: 1000 }, (_, i) => i);

for (const chunk of chunk(largeArray, 100)) {
  console.log(`Processing chunk: ${chunk[0]} - ${chunk[chunk.length - 1]}`);
}

  // ... 中间省略 ...

// 使用
(async () => {
  for await (const chunk of processLargeFile('./large-file.bin')) {
    await processChunk(chunk);
  }
})();

5.6 状态机实现

javascript
// 简单状态机
function* stateMachine() {
  while (true) {
    yield 'idle';
    yield 'processing';
    yield 'done';
  }
}

const machine = stateMachine();
console.log(machine.next().value); // 'idle'
console.log(machine.next().value); // 'processing'

  // ... 中间省略 ...


const processor = taskProcessor();
processor.next(); // 初始化
console.log(processor.next({ type: 'add', task: 'Task 1' }).value); // ['Task 1']
console.log(processor.next({ type: 'add', task: 'Task 2' }).value); // ['Task 1', 'Task 2']
console.log(processor.next({ type: 'complete', task: 'Task 1' }).value); // ['Task 2']

5.7 协程实现

javascript
// 简单协程调度器
class Scheduler {
  constructor() {
    this.tasks = [];
  }
  
  add(generator) {
    this.tasks.push({
      iterator: generator(),
      state: 'ready'
    });
  }

  // ... 中间省略 ...

// Task A: 0
// Task B: 0
// Task A: 1
// Task B: 1
// Task A: 2
// Task B: 2

5.8 数据管道

javascript
// 生成器管道
function* numbers() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
}

function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {

  // ... 中间省略 ...

    x => x * x
  ),
  10
);

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

六、迭代器工具方法

6.1 自定义迭代器工具

javascript
// 迭代器工具类
class IteratorTools {
  // 生成范围
  static *range(start, end, step = 1) {
    for (let i = start; i < end; i += step) {
      yield i;
    }
  }
  
  // 重复值
  static *repeat(value, times = Infinity) {
    for (let i = 0; i < times; i++) {

  // ... 中间省略 ...

// 使用示例
console.log([...IteratorTools.range(0, 5)]); // [0, 1, 2, 3, 4]
console.log([...IteratorTools.repeat('hi', 3)]); // ['hi', 'hi', 'hi']
console.log([...IteratorTools.zip([1, 2], ['a', 'b'])]); // [[1, 'a'], [2, 'b']]
console.log([...IteratorTools.enumerate(['a', 'b'])]); // [[0, 'a'], [1, 'b']]
console.log([...IteratorTools.reverse([1, 2, 3])]); // [3, 2, 1]

6.2 Iterator Helpers(ES2025 内置)

ES2025 为所有内置迭代器原型(Iterator.prototype)新增了标准的辅助方法,无需再手动实现上述工具类。这些方法支持惰性链式调用,不会创建中间数组:

javascript
// ES2025 Iterator Helpers - 原生支持
function* naturals() {
  let i = 0
  while (true) yield i++
}

// 链式操作(惰性求值)
const result = naturals()
  .take(10)                    // 取前 10 个
  .filter(x => x % 2 === 0)   // 过滤偶数
  .map(x => x ** 2)           // 平方
  .drop(1)                    // 跳过第一个

  // ... 中间省略 ...

const expanded = [1, 2, 3].values().flatMap(x => [x, x * 10])
console.log([...expanded]) // [1, 10, 2, 20, 3, 30]

// from() - 从迭代器创建新的迭代器
const iter = Iterator.from([1, 2, 3])
console.log([...iter.map(x => x * 2)]) // [2, 4, 6]

Iterator Helper 方法完整列表:

方法说明返回类型惰性/即时
.map(fn)映射每个元素Iterator惰性
.filter(fn)过滤元素Iterator惰性
.take(n)取前 n 个Iterator惰性
.drop(n)跳过前 n 个Iterator惰性
.flatMap(fn)映射后扁平化Iterator惰性
.reduce(fn, init)累计计算即时
.toArray()转为数组Array即时
.forEach(fn)遍历undefined即时
.some(fn)存在性检测boolean即时
.every(fn)全量检测boolean即时
.find(fn)查找元素即时
Iterator.from(iterable)创建迭代器Iterator惰性

七、性能优化与最佳实践

7.1 性能考虑

内存效率

javascript
// ❌ 不好的做法:一次性加载所有数据
const allData = loadData(); // 占用大量内存
for (const item of allData) {
  process(item);
}

// ✅ 好的做法:使用生成器按需加载
function* loadDataLazy() {
  let page = 1;
  while (hasMore(page)) {
    const data = fetchPage(page++);
    for (const item of data) {
      yield item;
    }
  }
}

for (const item of loadDataLazy()) {
  process(item); // 每次只占用当前项的内存
}

提前终止

javascript
// 使用 return() 进行清理
function* readLines(file) {
  const fd = openFile(file);
  try {
    while (hasMore(fd)) {
      yield readLine(fd);
    }
  } finally {
    closeFile(fd); // 确保资源释放
  }
}

// for...of 会自动调用 return() 当使用 break/return 时
for (const line of readLines('data.txt')) {
  if (line.includes('ERROR')) {
    break; // 触发 finally,关闭文件
  }
}

7.2 最佳实践

1. 使用生成器简化迭代器创建

javascript
// ❌ 冗长的传统方式
const iterable1 = {
  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 { done: true };
      }
    };
  }
};

// ✅ 简洁的生成器方式
const iterable2 = {
  data: [1, 2, 3],
  *[Symbol.iterator]() {
    for (const item of this.data) {
      yield item;
    }
  }
};

2. 避免在生成器中保存外部状态

javascript
// ❌ 不好:依赖外部状态
let counter = 0;
function* badGenerator() {
  while (true) {
    yield counter++; // 外部可修改
  }
}

// ✅ 好:内部维护状态
function* goodGenerator() {
  let counter = 0;
  while (true) {
    yield counter++;
  }
}

3. 合理使用 yield*

javascript
// ✅ 委托给其他迭代器
function* flatten(arr) {
  for (const item of arr) {
    if (Array.isArray(item)) {
      yield* flatten(item);
    } else {
      yield item;
    }
  }
}

console.log([...flatten([1, [2, [3, 4]], 5])]); // [1, 2, 3, 4, 5]

4. 使用 for...of 而非手动迭代

javascript
const iterable = [1, 2, 3];

// ❌ 冗长
const iterator = iterable[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
  console.log(result.value);
  result = iterator.next();
}

// ✅ 简洁
for (const item of iterable) {
  console.log(item);
}

5. 处理异步数据流

javascript
// ✅ 使用异步生成器处理异步数据
async function* fetchAllPages(baseUrl) {
  let page = 1;
  while (true) {
    const response = await fetch(`${baseUrl}?page=${page}`);
    const data = await response.json();
    
    if (data.items.length === 0) break;
    
    yield data.items;
    page++;
  }
}

// 使用 for await...of
(async () => {
  for await (const items of fetchAllPages('/api/data')) {
    for (const item of items) {
      console.log(item);
    }
  }
})();

7.3 性能对比

javascript
// 性能测试:生成器 vs 数组
function* generatorRange(size) {
  for (let i = 0; i < size; i++) {
    yield i;
  }
}

function arrayRange(size) {
  return Array.from({ length: size }, (_, i) => i);
}

// 测试内存占用
const size = 1000000;

// 生成器:几乎不占用内存
const gen = generatorRange(size);

// 数组:占用大量内存
const arr = arrayRange(size);

console.log(process.memoryUsage()); // 数组方式内存占用明显更高

7.4 使用场景指南

场景推荐方案原因
大数据集处理生成器按需生成,节省内存
无限序列生成器无法创建无限数组
文件流处理异步生成器逐块处理,不阻塞
树结构遍历生成器 + yield*简化递归代码
数据转换管道生成器懒加载,可组合
简单数组遍历for...of性能最优,代码简洁
需要随机访问数组生成器不支持索引访问

八、常见问题与陷阱

8.1 生成器是一次性的

javascript
const arr = ['a', 'b', 'c'];

// for...of:遍历值
for (const value of arr) {
  console.log(value); // 'a', 'b', 'c'
}

// for...in:遍历键(索引)
for (const key in arr) {
  console.log(key); // '0', '1', '2'
}

const obj = { a: 1, b: 2 };

// for...of:不能直接遍历普通对象
// for (const value of obj) { } // TypeError

// for...in:可以遍历对象
for (const key in obj) {
  console.log(key, obj[key]); // 'a' 1, 'b' 2
}

六、手动使用迭代器

javascript
const arr = [1, 2, 3];

// 获取迭代器
const iterator = arr[Symbol.iterator]();

// 手动迭代
let result = iterator.next();
while (!result.done) {
  console.log(result.value);
  result = iterator.next();
}

七、注意事项

javascript
function* generator() {
  yield 1;
  yield 2;
}

const gen = generator();
console.log([...gen]); // [1, 2]
console.log([...gen]); // [](已经消耗完毕)

// ✅ 解决方案:重新创建
const gen2 = generator();
console.log([...gen2]); // [1, 2]

// ✅ 或创建工厂函数
function createGenerator() {
  return generator();
}

const gen3 = createGenerator();
console.log([...gen3]); // [1, 2]

8.2 第一次 next() 的值被忽略

javascript
function* generator() {
  const value = yield 1;
  console.log(value);
}

const gen = generator();

// ❌ 错误:第一次传入的值被忽略
gen.next('ignored'); // 启动生成器,执行到 yield 1

// ✅ 正确:从第二次开始传值
gen.next('received'); // received

8.3 不能使用箭头函数

javascript
// ❌ 错误:箭头函数不能作为生成器
const gen = *() => {
  yield 1;
}; // SyntaxError

// ✅ 正确:使用普通函数
function* gen() {
  yield 1;
}

// ✅ 或使用函数表达式
const gen2 = function*() {
  yield 1;
};

8.4 生成器中的 this

javascript
// ❌ 问题:生成器函数中的 this
function* generator() {
  yield this.value; // this 可能不是预期的对象
}

const obj = {
  value: 42,
  gen: generator
};

const gen = obj.gen();
console.log(gen.next()); // this 指向问题

  // ... 中间省略 ...

    yield this.value;
  }
};

const gen3 = obj3.gen();
console.log(gen3.next()); // { value: 42, done: false }

8.5 for...of 不能遍历普通对象

javascript
const obj = { a: 1, b: 2 };

// ❌ 错误:普通对象不可迭代
for (const item of obj) {
  console.log(item);
}
// TypeError: obj is not iterable

// ✅ 解决方案1:使用 for...in
for (const key in obj) {
  console.log(key, obj[key]);
}

  // ... 中间省略 ...

  }
};

for (const [key, value] of iterableObj) {
  console.log(key, value);
}

8.6 yield 表达式的优先级

javascript
// ❌ 错误:yield 优先级低
function* bad() {
  // yield 1 + 2 被解析为 (yield 1) + 2
  console.log(yield 1 + 2);
}

// ✅ 正确:使用括号明确优先级
function* good() {
  console.log((yield 1) + 2);
}

// ✅ 或先计算再 yield
function* good2() {
  const value = 1 + 2;
  yield value;
}

8.7 异步生成器的错误处理

javascript
async function* asyncGenerator() {
  // ❌ 错误可能未被正确捕获
  const data = await fetchData();
  yield data;
}

// ✅ 正确:添加错误处理
async function* safeAsyncGenerator() {
  try {
    const data = await fetchData();
    yield data;
  } catch (error) {
    console.error('Error:', error);
    yield { error: error.message };
  }
}

// 使用时也要捕获错误
(async () => {
  try {
    for await (const item of safeAsyncGenerator()) {
      console.log(item);
    }
  } catch (error) {
    console.error('Outer error:', error);
  }
})();

8.8 return 语句的影响

javascript
function* generator() {
  yield 1;
  return 'done'; // 提前返回
  yield 2;       // 不会执行
}

const gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 'done', done: true }

// 注意:return 的值会被 for...of 忽略
for (const item of generator()) {
  console.log(item); // 只输出 1
}

8.9 常见错误对比表

错误场景错误代码正确代码
生成器复用[...gen, ...gen][...generator(), ...generator()]
第一次 next 传值gen.next('value')gen.next(); gen.next('value')
箭头生成器const g = *() => {}const g = function*() {}
遍历普通对象for (const v of obj)for (const v of Object.values(obj))
yield 优先级yield a + byield (a + b)
生成器中的 thisyield this.value使用对象方法简写 *gen()

九、API 参考

9.1 迭代器协议

next() 方法

typescript
interface IteratorResult<T> {
  value: T | undefined;
  done: boolean;
}

interface Iterator<T> {
  next(value?: any): IteratorResult<T>;
  return?(value?: any): IteratorResult<T>;
  throw?(e?: any): IteratorResult<T>;
}

可迭代协议

typescript
interface Iterable<T> {
  [Symbol.iterator](): Iterator<T>;
}

9.2 生成器对象方法

方法参数返回值说明
next(value?)传递给 yield 的值{ value, done }恢复执行
return(value?)返回值{ value, done: true }终止生成器
throw(error)错误对象{ value, done }抛出错误

9.3 生成器函数语法

javascript
// 函数声明
function* name() { yield; }

// 函数表达式
const gen = function*() { yield; };

// 对象方法
const obj = {
  *method() { yield; }
};

// 类方法
class MyClass {
  *method() { yield; }
  static *staticMethod() { yield; }
}

9.4 for...of vs for...in

javascript
const arr = ['a', 'b', 'c'];

// for...of:遍历值
for (const value of arr) {
  console.log(value); // 'a', 'b', 'c'
}

// for...in:遍历键(索引)
for (const key in arr) {
  console.log(key); // '0', '1', '2'
}

const obj = { a: 1, b: 2 };

// for...of:不能直接遍历普通对象
// for (const value of obj) { } // TypeError

// for...in:可以遍历对象
for (const key in obj) {
  console.log(key, obj[key]); // 'a' 1, 'b' 2
}

对比表:

特性for...offor...in
遍历内容键(属性名)
适用对象可迭代对象可枚举对象
数组遍历元素值索引(字符串)
对象遍历❌ 不支持✅ 支持
Symbol 属性✅ 支持❌ 忽略
原型链属性❌ 忽略✅ 包含
性能较快较慢

// ... 中间省略 ...

  • 参数:每次 next(arg) 可以传入不同的值,作为 yield 表达式的结果
  • 执行体:由 next() 推动的多个可暂停片段
  • 结果:每次 yield 产出一个值,而非单次 return

执行机制

code
flowchart TD
    A["调用 generator()"] --> B["创建生成器对象 tor"]
    B --> C["状态: suspendedStart\n上下文: 创建但未压栈"]

    C --> D["tor.next()"]
    D --> E["将 tor.[[GeneratorContext]] 压入调用栈"]
    E --> F["执行到 yield value"]
    F --> G["挂起: 将上下文移出调用栈"]
    G --> H["状态: suspendedYield\n返回: { value, done: false }"]

    H --> I["tor.next(arg)"]
    I --> J["将上下文重新压入调用栈"]
    J --> K["arg 作为 yield 表达式的结果值"]
    K --> L["继续执行到下一个 yield 或 return"]

    L --> M{遇到什么?}
    M -->|yield| G
    M -->|return| N["状态: completed\n返回: { value, done: true }"]

    subgraph 双向数据流
        O["yield value → 传出"] --> P["外部获得 value"]
        P --> Q["next(arg) → 传入"]
        Q --> R["yield 表达式求值为 arg"]
    end

核心洞察

  1. 生成器桥接命令式迭代与函数式递归:递归将循环映射为函数调用的重复,迭代将循环映射为函数体的重复执行。生成器通过 yield 将迭代过程"函数式化"——把一系列迭代步骤转化为一系列函数调用(next()),每次调用都有独立的输入(arg)和输出(value)。

  2. x = yield x 展示了 yield 的双重身份

    • 作为输出接口yield x 向外部发送 x 的值
    • 作为输入接口x = ... 接收 next(arg) 传入的 arg 并赋值给 x
    • 这使得生成器的参数界面从传统的"单向传入"变为"双向通信"
  3. 生成器上下文在栈外:所有普通函数的执行上下文都在调用栈上,而生成器的上下文(多数时间)在栈的外面yield 挂起时,将执行现场(包括执行位置和环境)从栈上移除;next() 恢复时,再将上下文压回栈顶。这是生成器能"暂停"而不阻塞整个线程的关键。

  4. 生成器闭包保持重入间的状态:生成器在多次 next() 调用之间保持闭包状态——局部变量的值在 yield 挂起时被保留,next() 恢复时继续使用。这与函数闭包保持调用间状态是同一机制。

  5. "迭代过程的函数式化":生成器将迭代过程从命令式的循环语句转化为函数式的连续调用。每个 next() 调用都是一个独立的"函数执行",有自己的输入参数和输出结果,而生成器闭包则维护了这些"调用"之间的状态连续性。

代码实证

javascript
// 1. 双向通信:yield 既是输出也是输入
function* bidirectional(x = 5) {
  console.log('初始值:', x--); // 5(来自 generator() 调用)
  x = yield x;                 // 传出 4,接收外部传入的值
  console.log('接收值:', x);    // 100(来自 next(100))
  return x;
}

const tor = bidirectional();
const r1 = tor.next();         // 初始值: 5,返回 { value: 4, done: false }
console.log(r1.value);         // 4
const r2 = tor.next(100);      // 接收值: 100,返回 { value: 100, done: true }

  // ... 中间省略 ...

  }
}

const fib = fibonacci();
const first10 = Array.from({ length: 10 }, () => fib.next().value);
console.log(first10); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

与实战的关联

  1. 懒求值(Lazy Evaluation):生成器只在 next() 被调用时才计算下一个值,这使得处理无限序列或大数据集时不会一次性占用全部内存:
javascript
// 懒加载:只在需要时才获取下一页数据
async function* lazyPages(api) {
  let cursor = null;
  do {
    const { data, nextCursor } = await api.fetchPage(cursor);
    yield* data;
    cursor = nextCursor;
  } while (cursor);
}
  1. 状态机实现:生成器的双向通信能力使其天然适合实现状态机——每次 next(action) 传入动作,生成器根据当前状态和动作产出新状态:
javascript
function* trafficLight() {
  while (true) {
    const action = yield 'RED';
    if (action === 'next') {
      const action2 = yield 'GREEN';
      if (action2 === 'next') {
        const action3 = yield 'YELLOW';
      }
    }
  }
}
  1. Redux-Saga 模式:在 Redux-Saga 中,生成器被用于描述副作用(Side Effects)的执行流程。yield 产出的是"指令描述"(如 call(fn)put(action)),由 saga 中间件解释执行。这是生成器"双向通信"能力的经典应用——生成器描述"要做什么",中间件负责"怎么做"并返回结果。

十、总结

核心概念总结

概念语法说明使用场景
迭代器协议next()定义遍历规则自定义数据结构遍历

// ... 中间省略 ...

  • 不要用 for...of 遍历普通对象
  • 不要在生成器中依赖外部可变状态
  • 不要忽略异步生成器的错误处理

使用场景决策树

code
需要遍历数据?
├─ 数据在内存中
│  ├─ 数组/字符串等内置类型 → for...of
│  └─ 自定义数据结构 → 实现 [Symbol.iterator]
│
├─ 数据量很大/无限
│  ├─ 同步生成 → 生成器
│  └─ 异步加载 → 异步生成器
│
└─ 需要异步获取
   ├─ 一次性获取 → async/await + 数组
   └─ 流式获取 → 异步生成器 + for await...of

性能建议

  1. 内存优化: 生成器是懒执行的,适合大数据集
  2. 提前终止: 使用 breakreturn() 释放资源
  3. 避免不必要的计算: 只在需要时才计算下一个值
  4. 异步并发: 异步生成器可以结合 Promise.all 并发处理

兼容性

特性ChromeFirefoxSafariEdgeNode.js
迭代器✅ 51+✅ 53+✅ 10+✅ 12+✅ 6.5+
生成器✅ 39+✅ 26+✅ 10+✅ 12+✅ 4.0+
for...of✅ 38+✅ 13+✅ 7+✅ 12+✅ 0.12+
异步迭代器✅ 63+✅ 57+✅ 11+✅ 12+✅ 10.0+
异步生成器✅ 63+✅ 57+✅ 11+✅ 12+✅ 10.0+

💡 提示: 生成器提供了一种优雅的方式来创建迭代器,特别适合处理大数据集、异步流程和复杂的数据结构遍历。在现代 JavaScript 开发中,它已成为处理流式数据和实现懒加载的重要工具。

⚠️ 注意: 虽然生成器功能强大,但对于简单的数组遍历,传统的 for 循环或 forEach 方法在性能上可能更有优势。选择合适的工具解决特定问题才是最佳实践。