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 相关文章推荐
Jquery 表单取值赋值的一些基本操作
Oct 11 Javascript
js 判断checkbox是否选中的实现代码
Nov 23 Javascript
控制页面按钮在后台执行期间不重复提交的JS方法
Jun 24 Javascript
图片Slider 带左右按钮的js示例
Aug 30 Javascript
JavaScript Serializer序列化时间处理示例
Jul 31 Javascript
常用的jquery模板插件——jQuery Boilerplate介绍
Sep 23 Javascript
深入理解$.each和$(selector).each
May 15 Javascript
详解Vue爬坑之vuex初识
Jun 14 Javascript
javascript与PHP动态往类中添加方法对比
Mar 21 Javascript
JavaScript实现获取两个排序数组的中位数算法示例
Feb 26 Javascript
jquery ajax 请求小技巧实例分析
Nov 11 jQuery
基于JS判断对象是否是数组
Jan 10 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+redis实现商城秒杀功能
2020/11/19 PHP
PHP实现二叉树深度优先遍历(前序、中序、后序)和广度优先遍历(层次)实例详解
2018/04/20 PHP
实例化php类时传参的方法分析
2020/06/05 PHP
js继承 Base类的源码解析
2008/12/30 Javascript
node.js中的fs.writeFile方法使用说明
2014/12/14 Javascript
详解JavaScript的while循环的使用
2015/06/03 Javascript
Bootstrap模态框(modal)垂直居中的实例代码
2016/08/18 Javascript
javascript鼠标跟随运动3种效果(眼球效果,苹果菜单,方向跟随)
2016/10/27 Javascript
微信小程序 地图(map)实例详解
2016/11/16 Javascript
使用jquery+iframe做一个ajax上传效果(实例)
2017/08/24 jQuery
vue.js select下拉框绑定和取值方法
2018/03/03 Javascript
webpack手动配置React开发环境的步骤
2018/07/02 Javascript
JavaScript常见事件处理程序实例总结
2019/01/05 Javascript
微信小程序用户拒绝授权的处理方法详解
2019/09/20 Javascript
js实现GIF图片的分解和合成
2019/10/24 Javascript
js函数柯里化的方法和作用实例分析
2020/04/11 Javascript
介绍Python中的fabs()方法的使用
2015/05/14 Python
Python中http请求方法库汇总
2016/01/06 Python
Python制作钉钉加密/解密工具
2016/12/07 Python
详解Python读取配置文件模块ConfigParser
2017/05/11 Python
Python上下文管理器和with块详解
2017/09/09 Python
Python实现购物车程序
2018/04/16 Python
Python使用分布式锁的代码演示示例
2018/07/30 Python
详解如何从TensorFlow的mnist数据集导出手写体数字图片
2019/08/05 Python
python利用7z批量解压rar的实现
2019/08/07 Python
python+django+rest框架配置创建方法
2019/08/31 Python
如何为Python终端提供持久性历史记录
2019/09/03 Python
keras获得某一层或者某层权重的输出实例
2020/01/24 Python
html5.2 dialog简介详解
2018/02/27 HTML / CSS
兰蔻加拿大官方网站:Lancome加拿大
2016/08/05 全球购物
Vans荷兰官方网站:美国南加州的原创极限运动潮牌
2018/01/23 全球购物
优秀老员工获奖感言
2014/02/15 职场文书
欢迎领导标语
2014/06/27 职场文书
银行给客户的感谢信
2015/01/23 职场文书
土木工程生产实习心得体会
2016/01/22 职场文书
详细分析PHP7与PHP5区别
2021/06/26 PHP