ES6 趣探Generators(总结)

这段时间在忙着学习typescript,那么就复习到了Generators,这就来总结一下我的学习成果
在这里插入图片描述

ES6新特性

Generator 函数是 ES6 的新特性,它允许一个函数返回的可遍历对象生成多个值。学习generators之前,必须要先学习迭代器,如果没有学过迭代器,可以先去小补一下🕵️‍♀️不然后面会看不懂的,不过可以先收藏再回来补嘿嘿🥂

语法

在使用中出现 * 语法和一个新的关键词 yield

function*是一个用来创建generators函数的语法,调用一个generators函数会返回一个generators对象。而这个generators对象只遵循了迭代器的接口(即next() return() throw()函数),而关于关键字yield嘛,它的功能就比较复杂了,那就让我们开始吧!

在这里插入图片描述

“偷懒的”迭代器

这里让我们简单的认识一下generators函数,并用它来创建一个“非常懒惰”的迭代器:
a.js:

function* lazyMark(){
    let index = 0;
    while(index<3){
        yield index++;
    }
}
let gen = lazyMark();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

在这里插入图片描述
你是否看出了yield的其中一部分特性?
是的,yield的值被当成了value的值继续往下传递了!但是具体的特性接着看吧。

控制权在我手里

这是generators最让人舒服的地方,这里实际上就是它可以让一个正在执行的函数“立马暂停”,然后将是否继续往下执行的权利全权交给调用者。

上面讲到调用一个generators函数会返回一个generators对象。但是一个generators函数并不会在调用的时候执行,它就是简简单单的创建了一个generators对象仅此而已,调不调用还得看调用者。
例子:
a.js:

function* generator(){
    console.log("start!");
    yield 0;
    console.log("执行中...");
    yield 1;
    console.log("执行中...");
}
var iterator = generator();
console.log("开始测试...");
console.log(iterator);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

在这里插入图片描述

根据输出结果:
console.log(iterator);这一句语句的执行会发现:
(秘籍拿好,后面还要王炸呢🦘)

1️⃣函数体内部的东西并没有被执行!,而是返回了Object [Generator] {}这样的信息,这也应对了上面我们的结论。

2️⃣并且,只有使用next()方法,才会去执行函数体内部的东西。

3️⃣一旦遇到yield语句,generators函数就会暂停执行,只有在下一个next被调用的时候,generators函数才会恢复执行。并且之前的函数执行过了就不会再执行,直到遇到下一个yield的时候,就又会暂停,直到下次再被调用。

4️⃣因此,实际上generators函数的执行是由generators对象控制的。

再来看几个操作深入一下,这里的重点就是什么时候执行,执行了谁,谁没有被执行
在这里插入图片描述

王炸案例

双向通信

老规矩哈,直接上菜🥗
a.js:

function* generator(){
    var bar = yield "foo";
    // 以下是函数里的输出
    console.log("----generator函数里的输出----");
    console.log(bar); //bar!
    console.log("----generator函数里的输出结束----");
}
var iterator = generator();
console.log("开始测试...");
const foo = iterator.next();
console.log(foo.value); //foo
// 继续执行
const nextThing = iterator.next("xmonster");

在这里插入图片描述
(看到输出希望看官暂停几秒自己先思考一下)

在JavaScript中,generators的一项非常强大的功能就是允许双向通信,它包含:

  • 你可以通过iterator.next(value)控制yield表达式的结果
  • 当yield表达式使用iterator.throw(error)时,还可以抛出一个异常(下面的例子再介绍)
    在这里我们就是使用了第一种,我们控制了yield表达式的结果

源码理解

我们将上面的代码放到typescript官网的学习乐园中,探一探源码,上面的代码会变成:

var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
function generator() {
    var bar;
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0: return [4 /*yield*/, "foo"];
            case 1:
                bar = _a.sent();
                // 以下是函数里的输出
                console.log("----generator函数里的输出----");
                console.log(bar); //bar!
                console.log("----generator函数里的输出结束----");
                return [2 /*return*/];
        }
    });
}
var iterator = generator();
console.log("开始测试...");
var foo = iterator.next();
console.log(foo.value); //foo
// 继续执行
var nextThing = iterator.next("xmonster");

__generator非常复杂,没关系,让我们看重点,那就是function generator(),当我们调用的时候,会发现,这是一个switch语句,case0返回的就是yield的初始值foo,而当我们第二次调用的时候,这里出现了这句话:

bar = _a.sent();

说明,它可以接收一个参数,并且将这个参数赋给了bar!
我们这里虽然说是修改了yield的值,但是我觉得还是按照上面的理解比较好

抛入异常

a.js:

function* generator(){
    try {
        yield "foo";
    } catch (error) {
        console.log("----函数里的输出----");
        console.log(error.message);
        console.log("----函数里的输出结束----");   
    }
}
var iterator = generator();
console.log("开始测试...");
// 开始执行,获取第一个yield的值
var foo = iterator.next();
console.log(foo.value);  //foo
// 继续执行,引发异常“bar”
var nextThing = iterator.throw(new Error("bar"));

在这里插入图片描述
看一看源码,这里只展示function generator()

function generator() {
    var error_1;
    return __generator(this, function (_a) {
        switch (_a.label) {
            case 0:
                _a.trys.push([0, 2, , 3]);
                return [4 /*yield*/, "foo"];
            case 1:
                _a.sent();
                return [3 /*break*/, 3];
            case 2:
                error_1 = _a.sent();
                console.log("----函数里的输出----");
                console.log(error_1.message);
                console.log("----函数里的输出结束----");
                return [3 /*break*/, 3];
            case 3: return [2 /*return*/];
        }
    });
}

现在明白了吧!

error_1 = _a.sent();

在这里插入图片描述
总结如下:

  • 外部系统可以将值推入generators函数体
  • 外部系统可以将一个异常抛入generators函数体

好啦,这就是我对Generators的初步理解,如果有深入的话,我会继续更的,都看到这里了,不点赞再走吗?哈哈哈下次再见啦
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是X大魔王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值