迭代器与生成器
1.理解迭代
迭代是按顺序多次执行一段程序,有明确的终止条件。
循环是迭代机制的基础,通过循环可以实现迭代机制,而迭代
之前我们需要知道如何使用我们要迭代的对象,例如数组的索引,我们可以通过递增索引来遍历数组,但这并不适合其他数据结构。
对此ES5新增了,Array.prototype.forEach()
[1,2,3].forEach(item => console.log(item));
但也仅适用于数组
2.迭代器模式
对于实现了iterable接口的结构,我们称之为"可迭代对象",且可以通过迭代器"iterator"消费。
实现 Iterable 接口(可迭代协议)要求同时具备两种能力:支持迭代的自我识别能力和 创建实现Iterator 接口的对象的能力。在 ECMAScript 中,这意味着必须暴露一个属性 作为“默认迭代器”,而且这个属性必须使用特殊的 Symbol.iterator 作为键。这个默认 迭代器属性必须引用一个迭代器工厂函数,调用这个工厂函数必须返回一个新迭代器。
var arr = [1,2,3];
var it = arr[Symbol.iterator]();
it.next(); // { value: 1, done: false }
//调用迭代器next方法,返回返回一个包含两个属性的对象,分别是value和done,
value表示当前位置的值,done表示是否迭代完
it.next(); // { value: 2, done: false }
it.next(); // { value: 3, done: false }
it.next(); // { value: undefined, done: true }
3.自定义迭代器的例子
class Counter{
constructor(num){
this.num = num;
}
[Symbol.iterator](){
let count = 1;
return {
next(){
if(count <= num)
return {done:false,value:count++};
else
return {done:true,value:undefined};
}
return(){
console.log("提前退出");
}
};
}
}
let counter = Counter(3)
for(let i of counter){console.log(i);}
//1
//2
//3
//如果循环中断,break,continue,return,throw,但是还有值没消费,迭代器会自动调用return方法
4.生成器
// 生成器函数声明
function* generatorFn() {}
// 生成器函数表达式
let generatorFn = function* () {}
// 作为对象字面量方法的生成器函数
let foo = {
* generatorFn() {}
}
// 作为类实例方法的生成器函数
class Foo {
* generatorFn() {}
}
// 作为类静态方法的生成器函数
class Bar {
static * generatorFn() {}
}
//生成器函数中yield关键字,中断执行,它生成的值会出现在next()方法返回的对象中,
function* generatorFn() {
yield 'foo';
yield 'bar';
return 'baz';
}
let generatorObject = generatorFn();
console.log(generatorObject.next()); // { done: false, value: 'foo' }
console.log(generatorObject.next()); // { done: false, value: 'bar' }
console.log(generatorObject.next()); // { done: true, value: 'baz' }
- 生成器对象作为可迭代对象
在生成器对象上显式调用 next()方法的用处并不大。其实,如果把生成器对象当成可迭代对象,
那么使用起来会更方便:
function* generatorFn() {
yield 1;
yield 2;
yield 3;
}
for (const x of generatorFn()) {
console.log(x);
}
// 1
// 2
// 3
- yield*最有用的地方是实现递归操作,此时生成器可以产生自身。看下面的例子:
function* nTimes(n) {
if (n > 0) {
yield* nTimes(n - 1);
yield n - 1;
}
}
for (const x of nTimes(3)) {
console.log(x);
}
// 0
// 1
// 2
- 小结 生成器是一种特殊的函数,调用之后会返回一个生成器对象。生成器对象实现了 Iterable 接口, 因此可用在任何消费可迭代对象的地方。生成器的独特之处在于支持 yield 关键字,这个关键字能够 暂停执行生成器函数。使用 yield 关键字还可以通过 next()方法接收输入和产生输出。在加上星号之 后,yield 关键字可以将跟在它后面的可迭代对象序列化为一连串值。