《你不知道的JavaScript》:生成器中的生产者和迭代器

生成器的一种有趣用法是作为一种产生值的方式。而这也是“生成器”这个名称的最初使用场景。前面说过生成器函数每次调用都会创建一个迭代器实例。这个迭代器实例有next()方法,与生成器函数中的yield关键字组合可以完成消息传递。

那怎么理解这个生成器和迭代器呢?从字面意思理解,可以理解成生成器是值的生产者,而迭代器是值的索取者。生成器函数调用产生迭代器,迭代器用next()方法来执行生成器函数。

这里面需要注意一个关键点,迭代器每次next()要想实现当前值与前面一个值有特定的关系,就需要生成器能保持状态来记住其生成的最后一个值。

首先可以实现一个使用函数闭包的版本:

var clourseSomething = (function(){
    var nextVal;
    return function(){
        if(nextVal === undefined){
            nextVal = 1;
        }else {
            nextVal = ( nextVal * 3 ) + 6;
        }
        return nextVal;
    }
})()

clourseSomething();    // 1
clourseSomething();    // 9
clourseSomething();    // 33
clourseSomething();    // 105

使用闭包的话,从执行粒度上看函数完成一次执行时就已经计算出下一个值,但实际上按照正常思路,应该是希望直到下一次clourseSomething()调用发生时才计算下一个值(即nextVal)。否则通常来说对更持久化或比起简单数字资源更受限的生产者来说,这可能是资源泄露的一种设计。唔,这也是闭包所容易为人诟病的一点。

这个时候就可以采用一种常见的设计模式,迭代器模式。迭代器是一个定义良好的接口,用于从一个生产者一步步得到一系列值,就是每次想要从生产者得到下一个值时调用next()。

var something = (function () {
    var nextVal;

    return {
        //for..of..循环需要
        [Symbol.iterator]: function(){ return this; },

        //标准迭代器接口方法
        next: function () {
            if(nextVal === undefined){
                nextVal = 1;
            }else {
                nextVal = ( nextVal*3 ) + 10;
            }
            return {done: false, value: nextVal}
        }
    }
})()

console.log(something.next().value);      // 1
console.log(something.next().value);      // 13
console.log(something.next().value);      // 49

next()调用返回一个对象。这个对象有两个属性:done是一个布尔值,标识迭代器的完成状态;value中放置迭代值。

ES6中新增了for...of...方法,这意味着可以通过原生语法循环自动迭代标准迭代器(接上例somthing):

for(var v of something){
    console.log(v);

    //不要死循环
    if(v > 500){
        break;
    }
}
// 1  13  49  157  481  1453

for..of循环在每次迭代中自动调用next(),它不会向next()中传入任何值,并且会在接收done:true时停止。上例中迭代器something总是返回done:false,这个for…of循环会永远运行下去,所以在测试循环里放入break条件。

除了构造自己的迭代器,许多javascript的内建数据结构(从ES6开始),比如array,也有默认的迭代器:

var arr = [10, 2, 3, 4, 5];
for(var v of arr){
    console.log(v);
}
// 10  2  3  4  5

for...of循环向arr请求它的迭代器,并自动使用这个迭代器来迭代遍历arr的值。

可能有朋友对迭代器something中的Symbol.iterator属性不是很了解,它是迭代器的定义标识,是for...of..工作的基础,具体将在下篇解释。

喜欢本文请扫下方二维码,关注微信公众号: 前端小二,查看更多我写的文章哦,多谢支持。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值