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 相关文章推荐
Valerio 发布了 Mootools
Sep 23 Javascript
如何在一个页面显示多个百度地图
Apr 07 Javascript
在ASP.NET中使用JavaScript脚本的方法
Nov 12 Javascript
js函数参数设置默认值的一种变通实现方法
May 26 Javascript
浅谈javascript的Touch事件
Sep 27 Javascript
你应该知道的几类npm依赖包管理详解
Oct 06 Javascript
Vue组件通信的四种方式汇总
Feb 08 Javascript
AngularJS 事件发布机制
Aug 28 Javascript
又拍云 Node.js 实现文件上传、删除功能
Oct 28 Javascript
JS实现根据数组对象的某一属性排序操作示例
Jan 14 Javascript
详解vue2.0模拟后台json数据
May 16 Javascript
js实现自定义右键菜单
May 18 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
js中几种去掉字串左右空格的方法
2006/12/25 Javascript
[原创]站长必须要知道的javascript广告代码
2007/05/30 Javascript
Javascript 构造函数,公有,私有特权和静态成员定义方法
2009/11/30 Javascript
javascript AOP 实现ajax回调函数使用比较方便
2010/11/20 Javascript
NodeJS的url截取模块url-extract的使用实例
2013/11/18 NodeJs
Jquery实现的角色左右选择特效
2014/05/21 Javascript
影响jQuery使用的14个方面
2014/09/01 Javascript
javascript瀑布流式图片懒加载实例解析与优化
2016/02/23 Javascript
学习Angularjs分页指令
2016/07/01 Javascript
基于JavaScript实现滑动门效果
2017/03/16 Javascript
完美实现js拖拽效果 return false用法详解
2017/07/28 Javascript
Vue引入sass并配置全局变量的方法
2018/06/27 Javascript
又拍云 Node.js 实现文件上传、删除功能
2018/10/28 Javascript
微信小程序之几种常见的弹框提示信息实现详解
2019/07/11 Javascript
关于vue组件事件属性穿透详解
2019/10/28 Javascript
Vue引入Stylus知识点总结
2020/01/16 Javascript
Python创建模块及模块导入的方法
2015/05/27 Python
Python 中的with关键字使用详解
2016/09/11 Python
python3+PyQt5实现自定义窗口部件Counters
2018/04/20 Python
python迭代dict的key和value的方法
2018/07/06 Python
Python关于反射的实例代码分享
2020/02/20 Python
Python如何使用神经网络进行简单文本分类
2021/02/25 Python
借助HTML5 Canvas来绘制三角形和矩形等多边形的方法
2016/03/14 HTML / CSS
新闻专业大学生找工作的自我评价
2013/10/30 职场文书
大学军训自我鉴定
2013/12/15 职场文书
我的大学生活职业生涯规划
2014/01/02 职场文书
会议主持词
2014/03/17 职场文书
森林防火宣传标语
2014/06/27 职场文书
校园安全广播稿范文
2014/09/25 职场文书
员工评语范文
2014/12/31 职场文书
三孔导游词
2015/02/05 职场文书
万能检讨书开头与结尾怎么写
2015/02/17 职场文书
努力工作保证书
2015/02/28 职场文书
农村结婚典礼主持词
2015/06/29 职场文书
车位出租协议书范本
2016/03/19 职场文书
Go语言怎么使用变长参数函数
2022/07/15 Golang