迭代器协议和 Generator 是什么?怎样让普通对象支持 for...of?
先理解原理和示例,再练习完整口述与追问。
回答要点
可迭代对象有 Symbol.iterator 方法,返回带 next() 的迭代器;for...of、展开、解构、Array.from 都依赖它。Generator 用 function* 和 yield 暂停与恢复,自动实现迭代器协议。普通对象默认不可迭代。
原理拆解
迭代器的 next() 返回 { value, done };可迭代对象的 [Symbol.iterator]() 每次调用都应返回新的迭代器。消费方提前结束(break、return、抛错)时会调用迭代器的 return(),便于释放资源。数组、字符串、Map、Set、arguments、NodeList 都内置了迭代器,普通对象没有,因此 for...of 普通对象会抛 TypeError,for...in 遍历的是可枚举键。
Generator 函数调用后不立即执行,返回生成器对象。每次 next(value) 运行到下一个 yield 暂停,传入的 value 成为上一个 yield 表达式的结果;throw() 在暂停处抛错,return() 提前结束;yield* 把迭代委托给另一个可迭代对象。它适合惰性序列、树遍历和状态机,async/await 也可以理解为“Generator + 自动执行器”(见宝典 63)。
示例与推演
const range = {
from: 1,
to: 3,
*[Symbol.iterator]() {
for (let value = this.from; value <= this.to; value++) yield value
}
}
console.log([...range]) // [1, 2, 3]
const user = { name: 'Ada', age: 36 }
for (const [key, value] of Object.entries(user)) console.log(key, value)
range 的方法用 Generator 实现,每次展开都得到新的迭代器,所以可以反复遍历。只想遍历普通对象时,用 Object.entries 转成可迭代数组比改造对象更直接。
易错点
迭代器只能消费一次,把同一个迭代器传给两个循环,第二个拿不到数据;无限 Generator 不能直接展开。异步数据源要用 async 迭代器和 for await...of。
参考回答可直接练习口述
for...of 依赖迭代器协议:对象的 Symbol.iterator 返回一个有 next 的迭代器,next 给出 value 和 done。普通对象没有这个方法,所以不能直接 for...of,可以用 Object.entries 或给对象实现迭代器。Generator 用 yield 暂停执行,天然就是迭代器,适合惰性生成、树遍历;它的暂停恢复机制也是理解 async/await 的基础。