JavaScript 迭代器与生成器
- 前言:从 for…of 说起
- 理解迭代和迭代器模式
-
- 什么是迭代?
- 迭代器模式
- 迭代器模式示意图
- 可迭代协议与迭代器协议
-
- 可迭代协议(Iterable Protocol)
- 迭代器协议(Iterator Protocol)
- 自定义迭代器
- 提前终止迭代器
- 普通对象可迭代
- 生成器
-
- 生成器基础
- 生成器的几种形式
-
- 生成器函数声明
- 生成器函数表达式
- 作为对象方法的生成器
- 作为类方法的生成器
- yield 关键字
- 生成器的执行流程
- 生成器的双向通信
- 提前终止生成器
-
- return()
- throw()
- 生成器与迭代器的关系
- 用生成器实现 async/await 的 Polyfill
- 结语
当我们需要在 JavaScript 遍历数组、处理异步数据流,甚至实现自定义的数据结构时,迭代器和生成器是其中强大而优雅的工具。理解它们不仅能写出更简洁的代码,还能深入理解 JavaScript 异步编程的底层原理。
前言:从 for…of 说起
const arr = [1, 2, 3];
// 传统的遍历方式
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]); // 1, 2, 3
}
// for…of
for (const item of arr) {
console.log(item); // 1, 2, 3
}
当我们在使用 for…of 这背后发生了什么?为什么对象不能直接用 for…of?
这都和 JavaScript 的迭代协议有关。理解这个协议,我们就能让任何对象都支持这种优雅的遍历方式。
理解迭代和迭代器模式
什么是迭代?
迭代是指按照某种顺序逐个访问集合中的每个元素的过程。在开发中,我们几乎每天都在进行迭代操作:
// 1. 数组迭代
const arr = [1, 2, 3];
arr.forEach(task => console.log(task));
// 2. 字符串迭代
const str = 'hello';
for (const char of str) {
console.log(char);
}
// 3. Map/Set 迭代
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
console.log(key, value);
}
迭代器模式
迭代器模式是一种设计模式,它提供了一种方法,顺序访问一个聚合对象(可迭代对象)中的各个元素,而又不暴露该对象的内部表示。
迭代器模式示意图
┌─────────┐
│ 聚合对象 │
└─────────┘
│
▼
┌────────────┐ ┌─────────┐
│ 迭代器工厂 │───────→ │ 元素 │
└────────────┘ └─────────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ 迭代器 │───────────→ │ 元素 │
└─────────┘ └─────────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ 迭代器 │───────────→ │ 元素 │
└─────────┘ └─────────┘
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ … │ │ … │
└─────────┘ └─────────┘
可迭代协议与迭代器协议
JavaScript 通过两个协议定义了迭代行为:
可迭代协议(Iterable Protocol)
对象需要实现 Iterable 接口(可迭代协议),并暴露一个默认属性作为“默认迭代器”,而且这个属性必须使用特殊的 Symbol.interator 作为键。
迭代器协议(Iterator Protocol)
对象需要实现 next() 方法,返回一个 IteratorResult 对象,其中包括迭代器返回的下一个值;如果不调用 next() 方法,则无法知道迭代器的当前位置。
自定义迭代器
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
// 实现可迭代协议
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
// 返回迭代器对象(实现迭代器协议)
return {
next() {
if (current <= end) {
const value = current;
current += 1;
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
}
// 使用自定义迭代器
const range = new Range(1, 5);
const iterator = range[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
提前终止迭代器
上述代码中,我们自定义了一个迭代器,其实,它还有两个可选的方法,用来提前终止迭代器:return() 和 throw() :
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
// 实现可迭代协议
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
// 返回迭代器对象(实现迭代器协议)
return {
next() {
if (current <= end) {
const value = current;
current += 1;
return { value, done: false };
}
return { value: undefined, done: true };
},
// 可选:实现 return 方法,用于提前终止迭代
return() {
console.log('迭代提前终止');
return { done: true };
},
// 可选:实现 throw 方法,用于抛出异常
throw(error) {
console.log('迭代器抛出异常:', error);
return { done: true };
}
};
}
}
普通对象可迭代
在引言的例子中,我们有提到普通对象不能使用 for…of 迭代遍历,因此普通对象不属于可迭代对象,那我们应该如何做,才能让普通对象也可以迭代呢?
const obj = { a: 1, b: 2, c: 3 };
obj[Symbol.iterator] = function* () {
for (const key in this) {
if (this.hasOwnProperty(key)) {
yield [key, this[key]];
}
}
};
for (o of obj) {
console.log(o);
}
生成器
生成器基础
生成器ES6 引入的特殊函数,它可以暂停和恢复执行,是创建迭代器的强大工具。其形式是一个函数,函数名称前面加一个星号(*)表示它是一个生成器。
注:箭头函数不能用来定义生成器。
生成器的几种形式
生成器函数声明
function* generatorFunction() {
yield 1;
yield 2;
yield 3;
}
生成器函数表达式
const generatorExpr = function* () {
yield 1;
yield 2;
yield 3;
};
作为对象方法的生成器
const obj = {
*generatorMethod() {
yield '方法中的生成器';
}
};
作为类方法的生成器
class MyClass {
*generatorMethod() {
yield '类方法中的生成器';
}
}
标识生成器函数的星号不受两边空格的影响,即:function* generatorFunction(){} 、function * generatorFunction(){} 、function *generatorFunction(){} ,这几种写法都是等价的。
yield 关键字
yield 关键字可以让生成器停止和继续执行,是生成器中最有用的地方。生成器在遇到 yield 关键字之前都会正常执行;遇到后,会停止执行,函数作用域的状态会被保留。停止执行的生成器函数,只能通过生成器对象调用 next() 来恢复执行。
生成器的执行流程
我们可以通过一个简单的示例,来观察生成器的执行流程:
function* simpleGenerator() {
console.log('开始执行');
const a = yield '第一次 yield';
console.log('a =', a);
const b = yield '第二次 yield';
console.log('b =', b);
const c = yield '第三次 yield';
console.log('c =', c);
return '结束';
}
// 创建生成器对象(不会立即执行)
const gen = simpleGenerator();
// 第一次调用 next(),执行到第一个 yield
let result = gen.next();
// 第二次调用 next(),传入参数给第一个 yield
result = gen.next('参数A');
// 第三次调用 next(),传入参数给第二个 yield
result = gen.next('参数B');
// 第四次调用 next(),传入参数给第三个 yield
result = gen.next('参数C');
// 再次调用 next()
result = gen.next();
生成器的双向通信
上述生成器的执行流程的例子中,我们可以通过 next() 方法给生成器函数传参;同时,我们也可以接收 yield 关键字后面的返回值:
function* simpleGenerator() {
console.log('开始执行');
const a = yield '第一次 yield';
console.log('a =', a);
const b = yield '第二次 yield';
console.log('b =', b);
const c = yield '第三次 yield';
console.log('c =', c);
return '结束';
}
// 创建生成器对象(不会立即执行)
const gen = simpleGenerator();
// 第一次调用 next(),执行到第一个 yield
let result = gen.next();
console.log('result =', result.value); // 输出: result = 第一次 yield
// 第二次调用 next(),传入参数给第一个 yield
result = gen.next('参数A');
console.log('result =', result.value); // 输出: result = 第二次 yield
// 第三次调用 next(),传入参数给第二个 yield
result = gen.next('参数B');
console.log('result =', result.value); // 输出: result = 第三次 yield
// 第四次调用 next(),传入参数给第三个 yield
result = gen.next('参数C');
console.log('result =', result.value); // 输出: result = 结束
// 再次调用 next()
result = gen.next();
console.log('result =', result.value); // 输出: result = undefined
提前终止生成器
与迭代器类似,生成器也支持提前终止/关闭的概念:return() 和 throw() 。
return()
但与迭代器不同,所有的生成器对象都有 return() 方法,只要通过它进入关闭状态,就无法恢复了。
const gen = simpleGenerator();
console.log(gen.return('终止了')); // { value: '终止了', done: true }
console.log(gen.next('第一次 yield')); // { value: undefined, done: true }
throw()
throw() 方法会在暂停的时候,将一个提供的错误注入到生成器对象中。如果错误没有被处理,生成器就会关闭:
const gen = simpleGenerator();
try {
gen.throw(new Error('第一次 throw')); // 直接抛出错误,进入 catch 块
} catch (e) {
console.log('捕获到错误:', e.message); // 输出: 捕获到错误: 第一次 throw
}
console.log('继续执行gen:', gen.next()); // 输出: 继续执行gen: { value: undefined, done: true }
生成器与迭代器的关系
生成器作为迭代器工厂,会自动实现迭代器协议,同时生成器对象本身也是可迭代的:
function* createIterator(items) {
for (let i = 0; i < items.length; i++) {
yield items[i];
}
}
const iterator1 = createIterator([1, 2, 3]);
console.log(iterator1.next()); // { value: 1, done: false }
console.log(iterator1.next()); // { value: 2, done: false }
console.log(iterator1.next()); // { value: 3, done: false }
console.log(iterator1.next()); // { value: undefined, done: true }
// 生成器对象本身也是可迭代的
const iterableGen = createIterator(['a', 'b', 'c']);
for (const item of iterableGen) {
console.log(item); // a, b, c
}
用生成器实现 async/await 的 Polyfill
function asyncGenerator(generatorFn) {
return function (…args) {
const generator = generatorFn.apply(this, args);
return new Promise((resolve, reject) => {
function step(key, arg) {
let result;
try {
result = generator[key](arg);
} catch (error) {
reject(error);
return;
}
const { value, done } = result;
if (done) {
resolve(value);
} else {
// 假设 value 总是一个 Promise
Promise.resolve(value).then(
val => step('next', val),
err => step('throw', err)
);
}
}
step('next');
});
};
}
结语
迭代器和生成器是 JavaScript 中强大而优雅的特性。它们不仅提供了更灵活的迭代方式,还开启了异步编程和惰性求值的新范式。对于文章中错误的地方或者有任何问题,欢迎在评论区留言讨论!
网硕互联帮助中心



![基于python的人脸检测识别录像系统[python]-计算机毕业设计源码+LW文档-网硕互联帮助中心](https://www.wsisp.com/helps/wp-content/uploads/2026/02/20260210120457-698b1ee9bfff8-220x150.jpg)

评论前必须登录!
注册