javascript如何理解迭代器和生成器_它们如何工作【教程】

技术百科 紅蓮之龍 发布时间:2026-01-27 浏览:
迭代器的next()方法返回{value: ..., done: true/false}对象,done为true后必须稳定返回{value: undefined, done: true},value类型任意,判断结束唯看done。

迭代器对象的 next() 方法到底返回什么

迭代器的核心是一个有 next() 方法的对象,每次调用都返回形如 { value: ..., done: true/false } 的对象。重点不是“它能循环”,而是“它把状态封装在自身内部”——done: true 之后再调用 next(),仍应返回 { value: undefined, done: true },这是规范要求,不是可选行为。

常见错误是手动实现迭代器时忽略 done 的终态处理,导致后续调用返回 { value: ..., done: false } 或抛错。例如自定义数组迭代器,没在越界后稳定返回 done: true,就会让 for...of 循环多跑一次或卡住。

  • value 可以是任意类型,包括 undefined;判断是否结束必须看 done,不能靠 value === undefined
  • 原生数组、Map、Set 的迭代器都满足“多次调用 next() 不改变已返回项”,但自定义迭代器若依赖外部可变状态(比如共享的 index 变量),就可能不满足这一隐含契约
  • ES2025 起部分引擎对 done: false 后还继续返回值的迭代器更严格,会提前终止消费

生成器函数为什么能暂停执行

function* 声明的函数不立即运行,而是返回一个迭代器对象;每次调用它的 next(),才从上次 yield 暂停处恢复,并执行到下一个 yieldreturn。关键在于:函数体内的执行上下文(变量、作用域、指令指针)被引擎保存在生成器对象内部,不是靠闭包模拟。

容易误解的是把 yield 当作“返回并丢弃状态”。实际上:yield 表达式本身有返回值(由下一次 next(arg)arg 决定),且函数暂停时所有局部变量依然存活。

  • 第一次 next() 进入函数体,执行到第一个 yield 就暂停;该 yield 右侧表达式的结果成为本次 value
  • 第二次 next('hello'),函数体内 yield 表达式的值就是 'hello',可用于赋值:const x = yield 123 → 下次 next('hello')x === 'hello'
  • 生成器一旦执行到 return 或自然结束,后续 next() 都返回 { value: ..., done: true },无法重置

for...of 和扩展运算符如何触发迭代协议

它们不关心对象是不是数组,只检查对象是否有 [Symbol.iterator] 方法。如果存在,就调用它获取迭代器,然后反复调用 next() 直到 done: true。这意味着:任何实现了迭代协议的对象,都能被 for...of[...iterable]Array.from() 等直接消费。

典型陷阱是误以为“可遍历 = 有 length 和数字键”。比如普通对象 { a: 1, b: 2 } 即使加了 length 属性,也不可被 for...of 遍历,除非手动部署 [Symbol.iterator]

  • 原生字符串、TypedArray、arguments

    对象都内置了 [Symbol.iterator];普通对象没有,但可以自己加:obj[Symbol.iterator] = function*() { yield* Object.entries(this); }
  • 扩展运算符对迭代器是“一次性消费”,不会缓存结果;若迭代器有副作用(如读取流、生成随机数),重复展开会得到不同结果
  • yield* 是委托语法,不是语法糖——它把控制权完整交给另一个可迭代对象,期间任何 throw()return() 都会透传过去

生成器和异步代码混用时的常见断裂点

生成器本身是同步的,yield 不会让出事件循环。想用它写异步逻辑(如 yield fetch(...)),必须配合额外的执行器(如 co 库)或手动 Promise.then() 链。现代开发中,async/await 已取代这种模式,但理解断裂点仍有价值。

最常踩的坑是混淆“生成器暂停”和“异步等待”:在 yield Promise.resolve(42) 中,Promise 立即 resolve,生成器拿到的是已兑现的 Promise 对象,而非 42;要得到 42,得用 await 或手动 .then()

  • 生成器函数内不能直接 await,必须写成 async function*(异步生成器),此时 next() 返回 Promise,需 await iterator.next()
  • yield 后接 Promise 不会自动 await,这和 async/await 的语义完全不同
  • 调试时注意:生成器的堆栈跟踪通常不包含 yield 暂停点,V8 的 generator.next() 调用看起来像普通函数调用

真正难的不是写出能跑的生成器,而是判断某个场景里是否该用它——比如需要早停的搜索、资源分片加载、状态机建模时,生成器天然合适;而只是想等个接口,async/await 更直白,也更容易被团队理解和维护。


# ai  # 的是  # 是一个  # 这是  # 自定义  # 迭代  # 循环  # 对象  #   # javascript  # java  # 指针  # 字符串  # 接口  # 委托  # 为什么  #   # function  # 异步  # 事件  # this  # 体内  # 封装  # 作用域  # map  # throw  # 返回值  # 闭包  # Object  # 运算符  # 遍历  # for  # const  # 局部变量  # promise  # undefined  # Length  # Array  # 可迭代对象  # symbol  # 它把 


相关栏目: <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 AI推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 SEO优化<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 技术百科<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 谷歌推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 百度推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 网络营销<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 案例网站<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 精选文章<?muma echo $count; ?>

相关推荐

在线咨询

点击这里给我发消息QQ客服

在线咨询

免费通话

24h咨询:4006964355


如您有问题,可以咨询我们的24H咨询电话!

免费通话

微信扫一扫

微信联系
返回顶部