第七章 迭代器与生成器

我们把实现了Iterable接口的对象称为可迭代对象

迭代器模式

interface IteratorResult {
    // 表示是否还有更多值可以访问
    done: boolean;
    // 包含迭代器返回的当前值
    value: any;
}
interface Iterator {
    next(): IteratorResult;
}
interface Iterable {
    // 任何实现Iterable接口的对象都有一个Symbol.iterator属性,这个属性引用默认迭代器Iterator
    [Symbol.iterator](): Iterator
}

迭代器通过next()方法遍历数据,next方法返回两个属性:done和value。每个迭代器都能完成一次完整迭代,互相之间没有联系。

可迭代协议

很多内置类型都实现了Iterable接口:

字符串

数组

映射

集合

arguments对象

NodeList等DOM集合类型

数组解构

let arr = ['foo', 'bar', 'baz'];
let [a,b,c] = arr;
console.log(a,b,c); // foo,bar,baz

扩展操作符

let arr2 = [...arr];
console.log(arr2); // ['foo','bar,'baz']

自定义迭代器

提前终止迭代器

生成器

生成器的形式是一个函数,函数名称前面加一个* 表示它是一个生成器

调用生成器函数会返回一个生成器对象,生成器对象实现了Iterator接口和Iterable接口,next()方法可以执行生成器

通过yield中断执行

yield关键字可以让生成器停止和开始执行。生成器函数在遇到yield关键字之前会正常执行,遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数只能通过在生成器对象上调用next()方法来恢复执行。

没有yield的生成器,调用一次next()就会返回{done:true},函数体中遇到yield,生成器会停止,直到下次调用next()。

yield关键字只能在生成器函数内部使用,用在其他地方会抛出错误。

function* validGeneratorFn() {
    yield;
}
// 无效
function* invalidGeneratorFnA() {
    function a() {
        yield;
    }
}

yield可以返回值,通过yield返回的值,done为false,通过return返回的值,done为true。yield还可以当作中间参数使用,通过next()传递的值能通过yield接收

1、生成器对象作为可迭代对象

生成器也是一种可迭代对象,使用起来更加方便

function* generatorFn() { 
 yield 1; 
 yield 2; 
 yield 3; 
} 
for (const x of generatorFn()) { 
 console.log(x); 
} 
// 1 
// 2 
// 3

2、使用yield实现输入和输出

yield 关键字还可以作为函数的中间参数使用,上一次让 生成器函数暂停的 yield 关键字会接收到传给 next()方法的第一个值。第一次调用 next()传入的值不会被使用,因为这一次调用是为了开始执行生成器函数:

// 用于作为函数的中间参数
function* generatorFn(initial) { 
 console.log(initial); 
 console.log(yield); 
 console.log(yield); 
} 
let generatorObject = generatorFn('foo'); 
generatorObject.next('bar'); // foo 
generatorObject.next('baz'); // baz 
generatorObject.next('qux'); // qux 

// 用于输入和输出
function* generatorFn() { 
 return yield 'foo'; 
} 
let generatorObject = generatorFn(); 
console.log(generatorObject.next()); // { done: false, value: 'foo' } 
console.log(generatorObject.next('bar')); // { done: true, value: 'bar' } 

3、产生可迭代对象

可以使用*增强yield的行为,让它能够迭代一个可迭代对象。

yield * [1,2,3] 可以迭代3次