迭代的英文'iteration'意为重复,ESCMAScript规范新增了两个高级特性:迭代器和生成器
7.1 理解迭代
for的计数循环是一种最简单的迭代
数组是JS中有序结合最典型的例子,可以通过数组长度和索引去迭代数组,但是却并不理想,因为迭代前要知道如何使用数据结构,且遍历顺序并不是所有数据结构共有的
ES5新增了forEach,向通用迭代进了一步
let l = [1,2,3,4,5]
l.forEach((item)=>{console.log(item);})
7.2 迭代器模式
迭代器模式描述了一个方案,把有些数据结构成为可迭代对象,实现了正式的Iterable接口并且可以通过迭代器Iterator消费
迭代器是按需创建的一次性对象,每个迭代器都会关联一个可迭代对象
7.2.1 可迭代协议
实现可迭代协议需要同时具备两种能力:
支持迭代的自我识别能力
常见实现Iterator接口对象的能力
内置的实现了I特让步了接口的数据类型:
字符串 数组 映射 集合 arguments对象 NodeList等DOM集合
实现可迭代协议的所有类型都会自动兼容接受可迭代对象的任何语言特性,包括:
for-of循环 数组解构 扩展操作符 Array.from() 创建集合 创建映射 Promise.all()等
7.2.2 迭代器协议
迭代器API使用next()可以在可迭代对象中遍历数据.
next()方法返回的迭代器对象IteratorResult(),包含两个属性done和value,前者表示next()是否还能取得下一个值,done:true的状态称为"耗尽",value表示可迭代对象的下一个值(done:false的情况下)
迭代器维护者一个指向可迭代对象的引用,因此迭代器会阻止垃圾回收程序可迭代对象
7.2.3 自定义迭代器
class Counter {
constructor(limit) {
this.limit = limit
}
[Symbol.iterator]() {
let count = 1
let limit = this.limit
return {
next() {
if (count <= limit) {
return { done: false, value: count++ }
} else {
return { done: true, value: undefined }
}
},
};
}
}
7.2.4 提前终止迭代器
可选的return()方法用于指定迭代器提前关闭时执行的逻辑,可能的提前退出包括:
for-of循环通过break,continue,return或throw提前退出
解构操作没有消费全部值
return()必须返回一个有效的IteratResult值,正常情况下可以只返回{done:true}
如果迭代器没有关闭,则还可以继续从上一次离开的地方继续迭代
数组的迭代器时不能关闭的
7.3 生成器
生成器拥有在一个函数块内暂停和恢复代码的功能
7.3.1 生成器基础
生成器的形式是一个函数,在函数名字前面加 (*)号,只要是可以定义函数的地方就可以定义生成器
箭头函数不饿能用来定义生成器函数
调用生成器函数会生成一个生成器对象,一开始处于暂停的状态.与迭代器类似,生成器也实现了Iterator接口,也有next()方法,调用会让生成器开始或恢复执行
7.3.2 通过yield终端执行
生成器在遇到yield时,执行会停止,作用状态会保留,只能通过调用next()恢复执行
function * generatorFn(){
yield 'a'
yield 'b'
return 'c'
}
let generatorObject = generatorFn()
console.log(generatorObject.next());
console.log(generatorObject.next());
console.log(generatorObject.next());
在生成器上显式调用next()方法用处不大,但是把生成器当成可迭代对象,作用会更大
2. 使用yield实现输入输出
function * generatorFn(){
for(let i=0;i<10;i++){
yield i
}
}
let generatorObject = generatorFn()
for(const x of generatorFn()){
console.log(x);
}
3.产生可迭代对象 yield*
function* generatorFn() {
yield* [1, 2, 3]
}
// 等价于
function* generatorFn() {
for (const x of [1, 2, 3]) {
yield x
}
}
4.使用yield*实现递归
function* nTimes(n) {
yield* nTimes(n-1)
yield n-1
}
7.3.3 生成器作为默认迭代器
生成器格外适合作为默认迭代器
不仅实现了Iterable接口,而且函数生成器和默认迭代器被调用之后都会产生迭代器
class Foo {
constructor(){
this.values = [1,2,3]
}
* [Symbol.iterator](){
yield * this.values
}
}
const f = new Foo()
for(const x of f){
console.log(x);
}
7.3.4 提前终止生成器
1.return()
return方法会强制让生成器关闭,传给return()的值就是终止迭代器时对象的value
2. throw()
throw()方法会在暂停的时候提供一个错误到生成器对象中
如果错误未被处理,生成器就会关闭
如果处理器内部处理了该错误,生成器会恢复执行,但是会跳过对应的yield
如果生成器对象还未开始执行,那么throw()不会在函数内部被捕获,而是相当于在函数块外面抛出了错误