核心是分离数据访问与存储:客户端仅通过迭代器协议遍历,容器实现[symbol.iterator]返回全新迭代器实例,封装状态,next()返回{value, done}对象,隐藏底层结构,避免暴露内部字段或共享状态。

核心在于把“怎么取数据”和“数据在哪存”彻底分开——客户端只认迭代器协议,不碰容器内部字段或索引逻辑。
定义可迭代对象:实现 [Symbol.iterator] 方法
让容器对象声明自己“支持遍历”,但不暴露具体结构。关键点是每次调用 [Symbol.iterator] 必须返回一个**全新迭代器实例**,保证多次遍历互不影响。
- 不要缓存同一个迭代器对象,否则第二次
for...of会从上次中断处继续,甚至报错 - 内部状态(如当前索引、游标位置)应封装在迭代器实例内,而非容器上
- 示例:一个范围类
Range(1, 5)可返回基于yield的生成器,或手动实现next()的对象,但绝不暴露this.start或this.current给外部
迭代器对象必须符合标准协议
返回的对象需有 next() 方法,每次调用返回形如 { value: any, done: boolean } 的对象。这是 JavaScript 引擎识别迭代行为的唯一依据。
-
done: true后不可再返回有效值;再次调用next()应持续返回{ value: undefined, done: true } - 若需支持提前终止(如
break或异常退出),可在迭代器中监听return()或throw()方法(高级用法,非必需但更健壮) - 避免在
next()中做重操作(如网络请求、文件读取),除非明确设计为惰性求值;否则违背“按需访问”初衷
隐藏真实数据组织方式
无论底层是数组、链表、数据库游标、还是分页 API 响应,对外都呈现统一的 for...of 或展开语法接口。
- 数组-backed 容器:用索引递增,但索引变量仅存在于迭代器闭包中
- 树结构:在迭代器内部维护栈或队列模拟 DFS/BFS,外部看不到节点指针
- 远程数据源:迭代器每次
next()触发一次 fetch,自动处理分页参数与 token 刷新,用户只管“取下一条” - 敏感数据:可在
next()中插入校验、脱敏或权限检查,而无需修改容器本身
避免常见反模式
看似用了迭代器,实则暴露了结构,等于白搭。
- ❌ 在容器上直接暴露
items数组或nodes链表供外部遍历 - ❌ 迭代器
next()返回原始内部对象引用,导致外部可随意修改 - ❌ 把
iterator()设计成返回this(即容器自己),混淆职责边界 - ❌ 使用全局变量或静态属性保存遍历状态,破坏并发安全











