返回博客

Generator 和 Iterator:那些你不知道却一直在默默付出的幕后英雄

Iterator 和 Generator,是 JS 世界真正的幕后英雄

📝
大约 24 小时前
4 分钟阅读
62 次浏览

Generator 和 Iterator:那些你不知道却一直在默默付出的幕后英雄

当你用 for...of 遍历数组,当你写下一句优雅的 ...spread,当你轻松地用 async/await 管理异步流程——你可能从未意识到,在这些看似简单的语法糖背后,Quietly 工作的,正是 Iterator(迭代器)Generator(生成器)

它们不张扬、不喧嚣,却构成了现代 JavaScript“基础设施”的一部分。 如果没有它们,前端世界会瞬间变得粗糙、笨重、混乱。

今天,就让我们从“幕后工程师”的角度重新认识它们: 它们在哪里工作?工作得多辛苦?为什么我们几乎每天都在用它们,却意识不到?


一、你每天都在用它们:JS 的“通用插口”靠 Iterator 驱动

当你写下这些代码时:

js
for (const item of array) {} [...set] const [a, b] = array Promise.all(iterable) new Map(iterable)

你以为你只是在用“语法”。 但其实,引擎偷偷做了这件事:

找对象的 Symbol.iterator,拿到一个“迭代器”,再一点一点把数据取出来。

Iterator 就像一个“统一数据访问协议”,JS 的各种数据结构——Array、Set、Map、String、TypedArray、arguments 等——只要提供这个接口,就能被 JS 所有遍历工具支持。

这意味着:

  • 消费侧统一了:for...of 不用关心对象内部是什么结构
  • 生产侧可自由扩展:任何对象只要定义 Symbol.iterator 就能像数组一样被遍历

也就是说:

Iterator 在 JS 中扮演了一种类似 “USB 标准接口” 的角色 —— 所有东西,只要实现了它,就能即插即用。

它就是那个每天被插件、被调用、被 for...of 掏空,但从不抱怨的“底层劳动者”。


二、Generator:给普通函数装上一颗“心脏”

Generator 是 JS 中最被低估的能力之一。

它让函数具备了:

  • 暂停
  • 恢复
  • 双向通信
  • 惰性求值
  • 无限数据生成
  • 流程控制能力

如果把普通函数比作一段一口气跑完的录影带,那么 Generator 就是一段可暂停、可倒带、可插播、可互动的互动剧本。

看看它最核心的能力:

js
function* gen() { console.log("start") const name = yield "你的名字?" const age = yield `你好 ${name}, 年龄?` return `${name} / ${age}` } const g = gen() g.next() // -> 暂停,等待用户输入 g.next("张三") // -> 把数据塞回函数中 g.next(22)

是不是像协程?像状态机?像 async/await? 没错——async/await 的底层就是基于 Generator 模式演化而来(Promise + 自动执行器)。

Generator 的出现,让 JS 彻底撕掉了“单纯的脚本语言”的标签。


三、它们联手时更强:Generator = 可迭代对象的创造者

Generator 最大的隐藏技能之一:

它返回的对象,既是迭代器(iterator),又是可迭代对象(iterable)。

也就是说:

js
function* test() { yield 1 yield 2 } for (const v of test()) {} // 完美支持 [...test()] // 可以展开

不要小看这一点—— 这意味着:你只写一个函数,就能同时支持所有 JS 遍历机制。

并且它还支持委托:

js
function* g1() { yield 1; yield 2; } function* g2() { yield* g1(); yield 3; }

看似简单,实际意义却是:

你可以用 Generator 拼接数据管线、分层数据读取、模块化数据流。

这就是为什么像 Redux-Saga 等大型库都 heavily 依赖 Generator。


四、它们在“那些你以为无关的地方”默默付出

这里是真相:

👇 你以为跟它们无关的地方,其实都靠它们撑着。


(1)展开运算符 ...

js
[...map]

为了支持这行代码,JS 做了:

找 mapSymbol.iterator → 得到 entries 的迭代器 → 一个一个解构值

Iterator 完成 100% 的工作。


(2)解构赋值

js
const [a, b] = set

解构过程是“迭代摄取”,底层完全依赖迭代器。


(3)Promise.all / Promise.race / Promise.allSettled

很多人不知道:

Promise.all 参数不是数组,是 iterable。 也就是说:你传 Set、Map、Generator 都可以。

js
Promise.all(new Set([p1, p2, p3]))

完全依赖可迭代协议。


(4)for await...of(异步迭代器)

你以为 async iterator 很高级?

其实就是:

Iterator + Promise + 协议升级版


(5)async/await 的祖先 = Generator

async/await 的早期实现(如 co 库)如下:

js
function* task() { const user = yield fetchUser() const orders = yield fetchOrders(user.id) return orders }

是不是很眼熟? 所以:

Generator 不是过时,而是“功成身退”,成为 async/await 的底层哲学。


五、现实中它们还做了哪些脏活累活?

处理大文件或无限数据(惰性读取)

Generator 可以做到:

读取 1GB 文件?No Problem → 一行一行 yield,不占内存。

复杂状态机

每一个 yield 天然是一个“状态节点”。

自定义数据流管道(类 RxJS)

过滤 → 映射 → 限流 → take(n) 全部用 generator 写,非常优雅。


六、总结:Iterator 和 Generator,是 JS 世界真正的幕后英雄

如果没有它们:

  • for...of 会消失
  • 展开运算符半废
  • 解构能力大幅削弱
  • Map/Set 构造能力倒退
  • Promise.all 限制更多
  • async/await 根本无法诞生
  • 无限序列处理不可能
  • Redux-Saga 不能用
  • 你处理大文件会痛苦万分

它们是 JS 生态中的:

🏗 结构性基础设施 ⚙ 底层数据驱动器 🧵 控制流奠基者 🛠 高级语法的隐形支撑

周温

周温

全栈开发工程师,专注于前端技术栈和物联网应用开发。拥有丰富的 React、Next.js、Nest.js 项目经验,擅长构建高性能、可扩展的 Web 应用。热爱技术分享,致力于将复杂的技术问题转化为易懂的实践指南。

评论 (0)

请先登录后再发表评论

登录

还没有评论,来发表第一条吧!