• JavaScript学习笔记(十三)——生成器(generator)


    在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意!

    如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/学习。


    generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。

    我们先看一下函数的概念:

    一个函数是一段完整的代码,调用一个函数就是传入参数,然后返回结果。

    function foo(x) {
        return x + x;
    }
    
    var r = foo(1); // 调用foo函数

    在函数执行过程中,如果没有遇到 return 语句,控制权无法交回被调用的代码。(函数末尾如果没有return,就是隐含的return undefined;

    generator定义

    function* foo(x) {
        yield x + 1;
        yield x + 2;
        return x + 3;
    }

    generator与函数的区别

    generator由 function* 定义(注意多出的*号),并且,除了 return 语句,还可以用 yield 返回多次

    举个例子就容易理解了:

    著名的斐波拉切数列,它是由0,1开头:

    0 1 1 2 3 5 8 13 21 34 ...

    要编写一个产生斐波那契数列的函数,可以这么写:

    function fib(max) {
        var
            t,
            a = 0,
            b = 1,
            arr = [0, 1];
        while (arr.length < max) {
            [a, b] = [b, a + b];
            arr.push(b);
        }
        return arr;
    }
    
    // 测试:
    fib(5); // [0, 1, 1, 2, 3]
    fib(10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

    函数只能返回一次,所以必须返回一个Array

    但是,如果换成generator,就可以一次返回一个数,不断返回多次。用generator改写如下:

    'use strict'
    function* fib(max) {
        var
            t,
            a = 0,
            b = 1,
            n = 0;
        while (n < max) {
            yield a;
            [a, b] = [b, a + b];
            n ++;
        }
        return;
    }

    直接调用:

    fib(5); // fib {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}

    我们发现,直接调用一个generator和调用函数不一样, fib(5) 仅仅是创建了一个generator对象,还没有去执行它。

    调用generator对象有两个方法:

    一是不断地调用generator对象的 next() 方法:

    var f = fib(5);
    f.next(); // {value: 0, done: false}
    f.next(); // {value: 1, done: false}
    f.next(); // {value: 1, done: false}
    f.next(); // {value: 2, done: false}
    f.next(); // {value: 3, done: false}
    f.next(); // {value: undefined, done: true}

     next() 方法会执行generator的代码,然后,每次遇到 yield x; 就返回一个对象 {value: x, done: true/false} ,然后“暂停”。返回的 value 就是 yield 的返回值, done 表示这个generator是否已经执行结束了。如果 done 为 true ,则 value 就是 return 的返回值。

    当执行到 done 为 true 时,这个generator对象就已经全部执行完毕,不要再继续调用 next() 了。

    二是直接用 for......of 循环迭代generator对象,这种方式不需要我们自己加判断 done :

    for (var x of fib(10)) {
    console.log(x); // 依次输出0, 1, 1, 2, 3, ...
    }

    generator优点

    1.正是由于generator在执行过程中能多次返回,所以它看上去像一个可以记住执行状态的函数,利用这一点,可以通过写一个generator来实现需要用面向对象才能实现的功能。

    比如,用一个对象来保存状态,我们一般是用对象的属性来保存。这样很繁琐:

    var fib = {
        a: 0,
        b: 1,
        n: 0,
        max: 5,
        next: function () {
            var
                r = this.a,
                t = this.a + this.b;
            this.a = this.b;
            this.b = t;
            if (this.n < this.max) {
                this.n ++;
                return r;
            } else {
                return undefined;
            }
        }
    };

    2.generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。

    没有generator时,用AJAX时需要这么写代码:

    ajax('http://url-1', data1, function (err, result) {
        if (err) {
            return handle(err);
        }
        ajax('http://url-2', data2, function (err, result) {
            if (err) {
                return handle(err);
            }
            ajax('http://url-3', data3, function (err, result) {
                if (err) {
                    return handle(err);
                }
                return success(result);
            });
        });
    });

    回调越多,代码越难看。

    通过使用generator时,可以这么写:

    try {
        r1 = yield ajax('http://url-1', data1);
        r2 = yield ajax('http://url-2', data2);
        r3 = yield ajax('http://url-3', data3);
        success(r3);
    }
    catch (err) {
        handle(err);
    }

    看上去是同步的代码,实际上是异步的代码。

  • 相关阅读:
    【Java学习】向上和向下转型
    【Java学习】java抽象类的作用
    【Java学习】追踪
    【Java学习】@Override 解释
    【Java学习】Java 中带包(创建及引用)的类的编译与调试
    【Java学习】相关基础算法
    【Java学习】import和import static的区别
    【testNG学习】testng.xml文件
    Reshape the Matrix
    Distribute Candies
  • 原文地址:https://www.cnblogs.com/whucs2012/p/7922207.html
Copyright © 2020-2023  润新知