掌握 for...of、展开运算符和解构背后的协议,然后用生成器来产生惰性序列。
在 Kodokon 中打开本课有两个不同的协议管辖着迭代。如果一个对象在 Symbol.iterator 键下暴露了一个方法,它就是可迭代的;该方法必须返回一个迭代器,也就是一个带有 next() 方法、返回 { value, done } 的对象。所有消费序列的东西——for...of、展开运算符 ...、解构、Array.from、Promise.all——都要经过这些协议。因此你可以让任何对象都变得兼容。
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { value: undefined, done: true };
}
};
}
};
console.log([...range]); // [1, 2, 3]手写迭代器很啰嗦。生成器(function*)会自动产生一个迭代器:每个 yield 都会挂起执行并保留函数的局部状态。这种惰性求值让你能够对无限序列进行建模,只要有一个消费方限制所请求值的数量即可。
function* naturals() {
let n = 0;
while (true) yield n++;
}
function* take(iterable, count) {
let index = 0;
for (const value of iterable) {
if (index >= count) return;
yield value;
index += 1;
}
}
console.log([...take(naturals(), 4)]); // [0, 1, 2, 3]生成器迭代器还会暴露 return() 和 throw()。有一个常被忽视的规范细节:当你通过 break、return 或抛出异常离开 for...of 循环时,引擎会自动调用 iterator.return()。在生成器中,这会运行任何挂起的 finally 块——这是释放资源(文件、连接、锁)的理想位置。
function* readLines() {
try {
yield "line 1";
yield "line 2";
} finally {
console.log("cleanup");
}
}
for (const line of readLines()) {
console.log(line);
break; // triggers return(), hence the finally
}
// line 1, cleanuplength 属性和数字索引next() 对象的 Symbol.iterator 方法Array.prototypenext() 方法break 退出一个正在消费生成器的 for...of 循环时,会发生什么?throw()return(),从而运行生成器的 finally 块next() 时才会被计算