JavaScript中自定义对象支持for...of需实现可迭代协议:在对象上定义[Symbol.iterator]方法,返回符合迭代器协议的新实例,推荐用生成器函数避免手动管理状态。

JavaScript 中的迭代器和可迭代协议不是语法糖,而是语言级约定:只要对象有 [Symbol.iterator]() 方法且返回一个带 next() 的对象,它就能被 for...of、Array.from()、展开运算符等直接使用。
怎么让自定义对象支持 for...of?
核心就一条:在对象上定义 [Symbol.iterator] 方法,返回一个符合迭代器协议的对象。
-
[Symbol.iterator]必须是普通函数(不能是箭头函数),否则this绑定会出错 - 每次调用该方法,都得返回**新迭代器实例**,否则多个
for...of同时遍历会共享状态、互相干扰 - 最稳妥写法是用生成器函数:
*[Symbol.iterator]() { yield ... }—— 引擎自动处理状态、done、暂停恢复,几乎不会写错 - 如果手动写
next(),必须返回形如{ value: any, done: boolean }的对象;done: true后再调用next(),仍应返回{ value: undefined, done: true }
为什么 for...of {a:1, b:2} 会报错?
因为普通对象({})的原型链上没有 [Symbol.iterator] 方法,引擎找不到“怎么一步步取值”的入口,直接抛 TypeError: xxx is not iterable。
- 数组、字符串、
Map、Set等内置类型已预置该方法;Object.prototype没有,这是有意设计 - 想遍历对象的值,可以手写:
Object.values(this)或Object.keys(this).map(k => this[k]),但注意这会一次性生成数组,失去惰性 - 若真要让对象可迭代,别只改实例属性(比如
obj[Symbol.iterator] = ...),最好加在类原型或用Object.defineProperty显式设置,确保可枚举性不影响继承
手写 next() 容易踩哪些坑?
手动实现看似可控,实则边界条件多、状态管理易错,常见问题包括:
立即学习“Java免费学习笔记(深入)”;
- 忘记在循环末尾返回
{ done: true }→for...of死循环 - 在
[Symbol.iterator]外部闭包中复用同一个变量(如let i = 0写在外面),导致多次遍历共享计数器 -
next()返回了undefined或{ v: 123 }这类结构 → 引擎无法解析,报Iterator result undefined is not an object - 误以为迭代器本身天然可迭代 —— 实际上一个只含
next()的对象,若没实现自己的[Symbol.iterator],就不能再被for...of套娃使用
生成器函数为什么是首选?
用 function* 写 [Symbol.iterator],本质是把状态机交给 JS 引擎托管,省去手动维护游标、判断边界、返回结构等所有琐碎逻辑。
-
yield表达式自动对应一次next()调用,value就是 yield 的值,函数退出即done: true - 支持
if、while、甚至await(配合async function*做异步迭代) - 不支持 IE,但在所有现代浏览器(Chrome/Firefox/Safari/Edge)及 Node.js ≥ 12 中完全可用
- 性能上无额外开销;相比手写,代码更短、更可靠、更易读
真正难的不是“怎么写”,而是理解协议对状态隔离、返回结构、多次遍历的要求——这些细节一旦漏掉,问题往往出现在生产环境里,且很难复现。











