JavaScript之生成器_动力节点Java学院整理


Posted in Javascript onJune 30, 2017

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返回多次。

大多数同学立刻就晕了,generator就是能够返回多次的“函数”?返回多次有啥用?

还是举个栗子吧。

我们以一个著名的斐波那契数列为例,它由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) {
  t = a + b;
  a = b;
  b = t;
  arr.push(t);
 }
 return arr;
}

// 测试:
fib(5); // [0, 1, 1, 2, 3]
fib(10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

函数只能返回一次,所以必须返回一个Array。但是,如果换成generator,就可以一次返回一个数,不断返回多次。用generator改写如下:

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

直接调用试试:

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: true}

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

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

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

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

generator和普通函数相比,有什么用?

因为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;
  }
 }
};

用对象的属性来保存状态,相当繁琐。

generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码。这个好处要等到后面学了AJAX以后才能体会到。

没有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的美好时代,用AJAX时可以这么写:

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);
}

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

练习

要生成一个自增的ID,可以编写一个next_id()函数

Javascript 相关文章推荐
QQ登录简单实现代码
Mar 09 Javascript
浅谈Javascript Base64 加密解密
Dec 28 Javascript
jQuery中prop()方法用法实例
Jan 05 Javascript
javascript先序遍历DOM树的方法
Feb 27 Javascript
jQuery实现将div中滚动条滚动到指定位置的方法
Aug 10 Javascript
webpack2.0搭建前端项目的教程详解
Apr 05 Javascript
JavaScript对JSON数据进行排序和搜索
Jul 24 Javascript
Vue-Access-Control 前端用户权限控制解决方案
Dec 01 Javascript
JQuery Ajax动态加载Table数据的实例讲解
Aug 09 jQuery
Vue项目数据动态过滤实践及实现思路
Sep 11 Javascript
优雅的处理vue项目异常实战记录
Jun 05 Javascript
Vue 列表上下过渡效果的实例代码
Jun 25 Javascript
详解vue组件通信的三种方式
Jun 30 #Javascript
JavaScript实现瀑布流图片效果
Jun 30 #Javascript
十大 Node.js 的 Web 框架(快速提升工作效率)
Jun 30 #Javascript
vue.js移动端tab组件的封装实践实例
Jun 30 #Javascript
jQuery表单设置值的方法
Jun 30 #jQuery
JavaScript注册时密码强度校验代码
Jun 30 #Javascript
Bootstrap Table从零开始
Jun 30 #Javascript
You might like
php用ini_get获取php.ini里变量值的方法
2015/03/04 PHP
Laravel 6.2 中添加了可调用容器对象的方法
2019/10/22 PHP
jQuery $.extend()用法总结
2014/06/15 Javascript
Javascript Memoizer浅析
2014/10/16 Javascript
JavaScript获得页面base标签中url的方法
2015/04/03 Javascript
jquery实现选中单选按钮下拉伸缩效果
2015/08/06 Javascript
Highcharts学习之坐标轴
2016/08/02 Javascript
js, jQuery实现全选、反选功能
2017/03/08 Javascript
vue过渡和animate.css结合使用详解
2017/06/14 Javascript
原生JS实现瀑布流插件
2018/02/06 Javascript
vue学习笔记之过滤器的基本使用方法实例分析
2020/02/01 Javascript
ES6中Set和Map用法实例详解
2020/03/02 Javascript
MySQLdb ImportError: libmysqlclient.so.18解决方法
2014/08/21 Python
Python学习小技巧之列表项的拼接
2017/05/20 Python
python 计算数组中每个数字出现多少次--“Bucket”桶的思想
2017/12/19 Python
使用Anaconda3建立虚拟独立的python2.7环境方法
2018/06/11 Python
Python+selenium点击网页上指定坐标的实例
2019/07/05 Python
python 字典套字典或列表的示例
2019/12/16 Python
Python爬取腾讯视频评论的思路详解
2019/12/19 Python
python判断两个序列的成员是否一样的实例代码
2020/03/01 Python
Python判断字符串是否为空和null方法实例
2020/04/26 Python
Python小白不正确的使用类变量实例
2020/05/29 Python
python 如何调用 dubbo 接口
2020/09/24 Python
纯CSS3实现鼠标悬停提示气泡效果
2014/02/28 HTML / CSS
佳能加拿大网上商店:Canon eStore Canada
2018/04/04 全球购物
阿玛尼美妆英国官网:Giorgio Armani Beauty英国
2019/03/28 全球购物
应用电子专业学生的自我评价
2013/10/16 职场文书
学校后勤人员职责
2013/12/27 职场文书
教学实验楼管理制度
2014/02/01 职场文书
团支部推优材料
2014/05/21 职场文书
关于美容院的活动方案
2014/08/14 职场文书
考博导师推荐信范文
2015/03/27 职场文书
婚礼伴郎致辞
2015/07/28 职场文书
幼儿园托班教育随笔
2015/08/14 职场文书
工作一年自我鉴定
2019/06/20 职场文书
Python代码实现双链表
2022/05/25 Python