前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 热点
旧版
首页程序员面试题库生成器对象与迭代器协议异同
JavaScript函数与作用域

生成器对象和数组这种可迭代对象有什么不一样?

数组是可迭代对象,每次遍历都会通过 [Symbol.iterator]() 新建一个迭代器,所以可以反复遍历。生成器对象本身就是迭代器,它的 [Symbol.iterator]() 返回自己,所有遍历共用一个进度,遍历完一次就空了。

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

可迭代对象、迭代器、生成器对象

  1. 可迭代对象有 [Symbol.iterator]() 方法,数组、Map、字符串都是
  2. 迭代器有 next() 方法,每次返回 { value, done },自己记着位置
  3. 生成器对象既有 next(),[Symbol.iterator]() 又返回自身

生成器还比普通迭代器多了 return() 和 throw() 两个方法。迭代器协议里这两个是可选的,手写的迭代器不一定有。

核心回答

先记住这个答案

区别在于谁记录遍历进度。数组这类可迭代对象只负责「造迭代器」,每次 for...of 都会调用一次 [Symbol.iterator]() 拿到一个新迭代器,进度记在新迭代器里,所以可以反复遍历。生成器对象本身就是迭代器,同时它的 [Symbol.iterator]() 返回的是它自己,所以它也能被 for...of 遍历。但不管遍历几次,用的都是同一个进度,第一次遍历完,第二次就什么都拿不到了。

  • 可迭代对象有 [Symbol.iterator](),负责造迭代器
  • 迭代器有 next(),负责记住遍历到哪了
  • 生成器对象两者都是,[Symbol.iterator]() 返回它自己
  • 生成器对象只能遍历一次,想重来就重新调用生成器函数

同样遍历两次,结果不一样

这是面试里最能说明问题的对比。

数组和生成器对象各展开两次JavaScript
const arr = [1, 2, 3]
console.log([...arr].join(','), [...arr].join(',')) // 两次都完整

function* numbers() {
  yield 1
  yield 2
  yield 3
}
const gen = numbers()
console.log(gen[Symbol.iterator]() === gen) // 返回的是它自己
console.log(gen.next().value) // 先手动取走一个
console.log([...gen].join(',')) // 只剩后面两个
console.log([...gen].length) // 已经空了
console.log([...numbers()].join(',')) // 重新调用才有新的
查看输出与解释
1,2,3 1,2,3
true
1
2,3
0
1,2,3

数组展开两次都完整,因为每次展开都新建了一个数组迭代器。生成器对象先被 next() 取走了 1,展开时只剩 2 和 3,再展开一次就是空的。想从头开始,只能重新调用 numbers() 得到一个新的生成器对象。

两个协议分别要求什么

JavaScript 的遍历靠两个约定。

可迭代协议
对象上有一个 [Symbol.iterator] 方法,调用后返回一个迭代器。for...of、展开运算符、Array.from、解构赋值、Promise.all 都是先调用这个方法。
迭代器协议
对象上有一个 next() 方法,每次调用返回 { value, done }。done 为 true 表示结束。另外还可以有可选的 return() 方法,for...of 中途 break 或抛错时会调用它,让迭代器做清理。 生成器对象把两个协议都实现了,而且 return() 和 throw() 都有。所以它能当迭代器手动调用 next(),也能直接丢给 for...of。
回答前,多想一步

容易答错的地方

把可迭代对象和迭代器当成一个东西
可迭代对象是「能造出迭代器的东西」,迭代器是「正在遍历的那个游标」。数组是可迭代对象但不是迭代器,数组上没有 next() 方法。生成器对象恰好同时是两者。
把生成器对象缓存起来给多处使用
比如把 const list = numbers() 传给两个组件,一个组件遍历完,另一个就拿不到数据了,两处还会互相抢进度。需要多次遍历时,缓存生成器函数本身,或者先 [...gen] 转成数组。
试着用自己的话回答

面试官还会怎么问?

怎么写一个可以反复遍历的对象?

让 [Symbol.iterator] 每次都返回一个新的迭代器。最省事的写法是把它写成生成器方法:const range = { *[Symbol.iterator]() { for (let i = 0; i < 3; i++) yield i } }。每次 for...of 都会重新调用这个方法,得到一个新的生成器对象,所以能遍历任意多次。

普通对象为什么不能 for...of?

普通对象没有 [Symbol.iterator] 方法,不满足可迭代协议,for...of 会报 TypeError: obj is not iterable。可以用 Object.entries(obj) 转成数组再遍历,或者用 for...in 遍历键名。

数组的迭代器是什么?

arr[Symbol.iterator]() 返回的是一个 Array Iterator 对象,它和生成器对象一样,[Symbol.iterator]() 也返回自己,同样只能遍历一次。arr.values()、arr.entries() 返回的也是这种迭代器。区别只在于数组本身每次能造出新的。

从一道题,走向一组知识

把知识连起来

函数与作用域

生成器函数能用 new 调用吗?里面的 new.target 是什么?

生成器对象的原型是生成器函数的 prototype,这个属性存在却不能拿来 new。

函数与作用域

怎么从外部停掉一个生成器,或者往里面抛一个错误?

生成器比普通迭代器多出来的 return() 和 throw(),就是从外部停止它、往里抛错的方式。

参考资料

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

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

本题目录
  1. 先记住这个答案
  2. 同样遍历两次,结果不一样
  3. 两个协议分别要求什么
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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