JavaScript中的迭代器和生成器
2025年12月23日 16:35
先讲迭代器(Iterator):就是“能一个个往外拿东西的容器”
你可以把迭代器想象成自动售货机:
- 你先有一堆商品(比如数组 [1,2,3]),把它们放进售货机里,这个售货机就是迭代器;
- 你每次按“出货”按钮(调用
next()方法),它就给你出一个商品,出完一个就少一个; - 等商品出完了,再按按钮就会返回
{ value: undefined, done: true }(提示“没货了”); - 而且它只能单向前进,出了2就回不到1了,不能倒着拿。
JavaScript 迭代器的简单例子
// 1. 先准备一个数组(一堆商品)
const nums = [1, 2, 3];
// 2. 把数组变成迭代器(装进售货机)
const it = nums[Symbol.iterator]();
// 3. 按按钮拿东西(调用 next())
console.log(it.next()); // 输出 { value: 1, done: false }(拿第一个)
console.log(it.next()); // 输出 { value: 2, done: false }(拿第二个)
console.log(it.next()); // 输出 { value: 3, done: false }(拿第三个)
console.log(it.next()); // 输出 { value: undefined, done: true }(没货了)
迭代器的核心特点
- 必须有一个
next()方法,调用后返回固定格式:{ value: 具体值, done: 是否迭代完 }; - 惰性获取:只有调用
next()才会返回下一个值,不会一次性把所有值加载到内存; - 用完就没:只能往前,不能回头,也不能重复用。
手动实现一个简单迭代器(理解原理)
就像自己造一个简易售货机:
// 手动实现迭代器:模拟售货机逻辑
function createIterator(arr) {
let index = 0; // 记录当前拿到第几个
return {
// 核心的 next() 方法
next: function() {
// 如果没拿完,返回当前值 + 未完成;否则返回 undefined + 已完成
if (index < arr.length) {
return { value: arr[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
// 使用这个自定义迭代器
const myIt = createIterator([10, 20, 30]);
console.log(myIt.next()); // { value: 10, done: false }
console.log(myIt.next()); // { value: 20, done: false }
console.log(myIt.next()); // { value: 30, done: false }
console.log(myIt.next()); // { value: undefined, done: true }
再讲生成器:“自动造东西的迭代器”
生成器是迭代器的升级版,你可以把它想象成现做现卖的小吃摊:
- 迭代器是“先把所有东西准备好再往外拿”(比如先把1、2、3都放进售货机);
- 生成器是“你要一个,我才做一个”(比如你要第一个包子,我才包第一个,要第二个才包第二个);
- 它不用提前把所有数据存在内存里,而是“按需生成”,特别省内存。
JavaScript 生成器的核心:function* + yield
function* 是生成器函数的标识(注意多了个 *),yield 就是“暂停并返回”的意思——比如你去小吃摊买包子,老板包一个给你,然后暂停,等你要下一个再继续包。
生成器的完整例子(最常用)
// 定义生成器函数(注意 function* 和 yield)
function* makeBuns() {
console.log("开始包第一个包子");
yield 1; // 包好第一个,返回,暂停
console.log("开始包第二个包子");
yield 2; // 继续,包第二个,返回,暂停
console.log("开始包第三个包子");
yield 3; // 继续,包第三个,返回,暂停
}
// 调用函数,得到生成器(此时函数不会执行,只是创建生成器)
const bunGen = makeBuns();
// 第一次拿:执行到第一个yield,返回 { value: 1, done: false }
console.log(bunGen.next()); // 输出:开始包第一个包子 → { value: 1, done: false }
// 第二次拿:从暂停的地方继续,执行到第二个yield
console.log(bunGen.next()); // 输出:开始包第二个包子 → { value: 2, done: false }
// 第三次拿:继续,执行到第三个yield
console.log(bunGen.next()); // 输出:开始包第三个包子 → { value: 3, done: false }
// 第四次拿:没包子了,返回 done: true
console.log(bunGen.next()); // 输出:{ value: undefined, done: true }
生成器的实用场景:按需生成大量数据
比如要生成 100 万个数字,用数组会占满内存,但生成器只在需要时计算:
// 生成器:按需生成数字,不占内存
function* generateBigNumbers(max) {
let num = 1;
while (num <= max) {
yield num++; // 要一个,生成一个
}
}
// 生成 100 万个数字的生成器(此时还没生成任何数字)
const bigNumGen = generateBigNumbers(1000000);
// 只拿前3个,后面的不会生成
console.log(bigNumGen.next().value); // 1
console.log(bigNumGen.next().value); // 2
console.log(bigNumGen.next().value); // 3
迭代器 vs 生成器 一句话区分(JS 版本)
- 迭代器:已有数据的“搬运工”(把现成的东西一个个拿出来,比如数组迭代器);
- 生成器:按需生成数据的“生产者”(没有现成数据,要的时候才造,用
function*+yield); - 生成器本质上是一种“自动实现了迭代器接口”的迭代器,写起来比手动迭代器简单 10 倍。
总结
- 迭代器:像自动售货机,提前装好物,按一次出一个,出完为止,核心是“遍历已有数据”;
- 生成器:像现做现卖的小吃摊,不用提前备货,要一个做一个,核心是“按需生成数据”,更省内存;
- JS 里迭代器靠
next()方法和{ value, done }格式,生成器靠function*+yield,两者都是“惰性计算”,适合处理大数据。