迭代器与生成器(js)

438 阅读2分钟

迭代器与生成器

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 关键字可以将跟在它后面的可迭代对象序列化为一连串值。