前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库yield* 委托可迭代对象
JavaScript函数与作用域

生成器里的 yield* 是干什么的?和 yield 有什么区别?

yield* iterable 会把另一个可迭代对象(数组、字符串、另一个生成器)的值逐个交给外面的消费者,全部交完才继续往下执行。yield 只会把整个对象当成一个值交出去。被委托生成器 return 的值不会出现在序列里,而是成为 yield* 表达式的结果。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#函数与作用域
先看核心答案读代码示例
理解线索

`yield*` 做了哪三件事

  1. 转交产出值内部每产出一个值,外层就原样交给消费者一次
  2. 转发 next 参数消费者调用 next(x) 传入的值直接送到内部生成器
  3. 接收返回值内部结束时 return 的值成为 yield* 表达式的结果

return 的值不会被 for...of 和展开运算符拿到。它们看到 done: true 就停,那一项的 value 被直接丢掉。

核心回答

先记住这个答案

yield* 是委托:在一个生成器里写 yield* other,就相当于把 other 产出的每个值都原样交给外面,等 other 迭代完了再继续执行后面的代码。other 可以是任何可迭代对象,数组、字符串、Set、另一个生成器都行。普通的 yield [1, 2] 只会交出一个数组,yield* [1, 2] 交出的是 1 和 2 两个值。如果被委托的是生成器,它最后 return 的值不会出现在序列里,而是作为 yield* 表达式的结果返回给外层。

  • yield* 逐个转交,yield 整个交出去
  • 数组、字符串、Map、生成器都能被 yield* 委托
  • 被委托生成器的 return 值是 yield* 的返回值
  • 递归遍历树用 yield* 最省事

yield 和 yield* 放在一起对比

最直接的办法是把两种写法的产出都展开看看。

两种写法的产出和返回值JavaScript
function* inner() {
  yield 1
  yield 2
  return 'inner done' // 不会出现在序列里
}

function* withYield() {
  yield [1, 2] // 整个数组作为一个值
}

function* withStar() {
  yield* 'ab' // 字符串逐个字符转交
  const result = yield* inner() // 1 和 2 被转交,return 值赋给 result
  yield result
}

console.log([...withYield()].length)
console.log([...withStar()].join(','))
查看输出与解释
1
a,b,1,2,inner done

withYield 只产出一个值,就是数组本身。withStar 先把字符串 'ab' 拆成两个字符交出去,再把 inner 的 1 和 2 交出去。inner 的 return 值没有被展开运算符看到,而是赋给了 result,外层自己再 yield 一次才出现在结果里。

最常见的用法:递归遍历树

树形菜单、嵌套评论、文件目录这类数据,用 yield* 写遍历特别自然:先交出当前节点,再对每个子节点递归委托。

前序遍历一棵菜单树JavaScript
function* walk(node) {
  yield node.name
  for (const child of node.children ?? []) {
    yield* walk(child) // 把子树的所有节点逐个转交出去
  }
}

const menu = {
  name: '首页',
  children: [
    { name: '文档', children: [{ name: 'API' }] },
    { name: '博客' }
  ]
}

console.log([...walk(menu)].join(' > '))
查看输出与解释
首页 > 文档 > API > 博客

没有 yield* 的话要自己写 for (const x of walk(child)) yield x,效果一样但啰嗦。生成器还是惰性的,消费者只取前几项时,后面的子树根本不会被遍历,适合只找第一个匹配节点的场景。

递归太深时要注意
每层 yield* 都嵌套了一个生成器,树特别深时还是会有调用栈压力,每取一个值也要穿过所有层。层级很深的数据改用显式栈循环更稳。
回答前,多想一步

容易答错的地方

以为 yield* 只能委托生成器
任何实现了 Symbol.iterator 的对象都可以,数组、字符串、Map、Set、arguments 都行。反过来,普通对象 { a: 1 } 没有实现迭代协议,yield* 它会抛 TypeError。
以为内部的 return 值会被 for...of 拿到
for...of 和展开运算符遇到 done: true 就停止,那一项的 value 直接丢弃。想拿到它,只能在外层生成器里用 const r = yield* inner() 接住。
试着用自己的话回答

面试官还会怎么问?

调用外层的 next(x),参数会传给谁?

委托期间传给内部生成器。外层停在 yield* 上时,消费者 next(x) 传入的 x 会原样作为内部生成器里当前 yield 表达式的值。外层自己在委托结束前拿不到这个参数。

外层调用 throw() 或 return() 会怎样?

也会转发给内部。throw(err) 会在内部当前暂停的位置抛出错误,内部有 try...catch 就能接住继续运行;return() 会让内部提前结束,执行它的 finally。如果内部迭代器没有 throw 方法,运行时会先关闭它,再抛出 TypeError。

异步生成器里能用 yield* 吗?

能。async function* 里的 yield* 可以委托另一个异步生成器,也可以委托普通的同步可迭代对象,消费时用 for await...of。

从一道题,走向一组知识

把知识连起来

模块与语言机制

模块里写了顶层 await,为什么导入它的父模块要一直等?能避免吗?

异步生成器里可以写 yield* 委托另一个异步迭代器,和顶层 await 一样都建立在 Promise 的暂停与恢复上。

模块与语言机制

ESM 遇到循环依赖是怎么处理的?和 CommonJS 拿到半成品对象有什么不同?

两题都关心执行顺序,生成器按 next 一步步推进,模块按依赖图深度优先执行。

参考资料

  • JavaScript:函数与作用域参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. yield 和 yield* 放在一起对比
  3. 最常见的用法:递归遍历树
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑