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 暂停处恢复,并执行到下一个 yield 或 return。关键在于:函数体内的执行上下文(变量、作用域、指令指针)被引擎保存在生成器对象内部,不是靠闭包模拟。
容易误解的是把 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; ?>
】
相关推荐
- Windows11如何设置专注助手_Windows
- Win10怎样清理C盘浏览器缓存_Win10清理浏
- Win11讲述人怎么关闭_Win11误触开启语音朗
- Win11怎么关闭系统推荐内容_Windows11
- C++如何使用std::transform批量处理
- Win11如何设置开机问候语 Win11修改登录界
- Win10如何卸载预装Edge扩展_Win10卸载
- Win11怎么更改鼠标指针_Windows 11自
- Linux如何使用Curl发送请求_Linux下A
- Mac如何开启夜览模式_Mac护眼模式设置与定时
- Win11怎样激活系统密钥_Win11系统密钥激活
- Win11怎么设置任务栏图标大小_Windows1
- 如何使用Golang管理模块版本_Golanggo
- Python列表推导式与字典推导式教程_简化代码高
- 如何使用正则表达式提取以编号开头、后接多个注解的逻
- Windows 10怎么隐藏特定更新补丁_Wind
- Win10系统怎么查看网络连接状态_Windows
- Python邮件系统自动化教程_批量发送解析与模板
- 如何使用Golang实现路由参数绑定_使用Mux和
- Windows服务启动类型恢复方法_错误修改导致的
- Mac系统更新下载慢或失败怎么办_解决macOS升
- mac怎么安装pip_MAC Python pip
- Win11文件扩展名怎么显示 Win11查看文件后
- windows系统找不到无线网络怎么办_windo
- MAC怎么设置程序窗口永远最前_MAC窗口置顶插件
- c++获取当前时间戳_c++ time函数使用详解
- php打包exe怎么传递参数_命令行参数接收方法【
- 如何使用Golang优化模块引入路径_Golang
- php转exe用什么工具打包快_高效打包软件推荐【
- 如何在Golang中实现服务熔断与限流_Golan
- Windows7如何安装系统镜像_Windows7
- 静态属性修改会影响所有实例吗_php作用域操作符下
- c++ namespace命名空间用法_c++避免
- Python正则表达式实战_模式匹配说明【教程】
- Python日志系统设计与实现_高可观测性架构实战
- Win11输入法选字框不见了怎么办_Win11输入
- Win11怎么开启移动热点_Windows11共享
- Win10路由器怎么隐藏ssid Win10隐藏w
- c++ atoi和atof函数用法_c++字符数组
- Win11如何开启telnet服务 Win11启用
- windows如何备份注册表_windows导出和
- PHP的Workerman对架构扩展有啥帮助_应用
- Win11怎么修改DNS服务器 Win11设置DN
- Golang如何遍历目录文件_Golang fil
- Win10如何更改网络连接_Windows10以太
- 如何在Golang中配置代码格式化工具_使用gof
- c++如何判断文件是否存在_c++ filesys
- php怎么下载安装并配置环境变量_命令行调用PHP
- Win11怎么禁用键盘自带键盘_Win11笔记本禁
- Win11怎么打开旧版计算器_Win11恢复传统计


QQ客服