阅读视图

发现新文章,点击刷新页面。

JavaScript中的迭代器和生成器

先讲迭代器(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 }(没货了)

迭代器的核心特点

  1. 必须有一个 next() 方法,调用后返回固定格式:{ value: 具体值, done: 是否迭代完 }
  2. 惰性获取:只有调用 next() 才会返回下一个值,不会一次性把所有值加载到内存;
  3. 用完就没:只能往前,不能回头,也不能重复用。

手动实现一个简单迭代器(理解原理)

就像自己造一个简易售货机:

// 手动实现迭代器:模拟售货机逻辑
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 倍。

总结

  1. 迭代器:像自动售货机,提前装好物,按一次出一个,出完为止,核心是“遍历已有数据”;
  2. 生成器:像现做现卖的小吃摊,不用提前备货,要一个做一个,核心是“按需生成数据”,更省内存;
  3. JS 里迭代器靠 next() 方法和 { value, done } 格式,生成器靠 function* + yield,两者都是“惰性计算”,适合处理大数据。
❌