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之日期选择器的深入解析
Jun 19 Javascript
jQuery学习笔记之jQuery+CSS3的浏览器兼容性
Jan 19 Javascript
jQuery layui常用方法介绍
Jul 25 Javascript
easyui 中的datagrid跨页勾选问题的实现方法
Jan 18 Javascript
Node.js调试技术总结分享
Mar 12 Javascript
关于Promise 异步编程的实例讲解
Sep 01 Javascript
JSON在Javascript中的使用(eval和JSON.parse的区别)详细解析
Sep 05 Javascript
JS中利用FileReader实现上传图片前本地预览功能
Mar 02 Javascript
用Node编写RESTful API接口的示例代码
Jul 04 Javascript
js中的数组对象排序分析
Dec 11 Javascript
JavaScript闭包相关知识解析
Oct 19 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
Apr 11 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 SQL防注入代码集合
2008/04/25 PHP
PHP中全局变量global和$GLOBALS[]的区别分析
2012/08/06 PHP
php遍历数组的4种方法总结
2014/07/05 PHP
简单实用的网站PHP缓存类实例
2014/07/18 PHP
PHP、Java des加密解密实例
2015/04/27 PHP
基于php的CMS中展示文章类实例分析
2015/06/18 PHP
JQuery 学习笔记 选择器之二
2009/07/23 Javascript
Javascript 汉字字节判断
2009/08/01 Javascript
javascript学习笔记(十八) 获得页面中的元素代码
2012/06/20 Javascript
js控制滚动条缓慢滚动到顶部实现代码
2013/03/20 Javascript
js获取元素到文档区域document的(横向、纵向)坐标的两种方法
2013/05/17 Javascript
jQuery中die()方法用法实例
2015/01/19 Javascript
jQuery拖动元素并对元素进行重新排序
2015/12/30 Javascript
js传值后台中文出现乱码的解决方法
2016/06/30 Javascript
Java  Spring 事务回滚详解
2016/10/17 Javascript
JS 中使用Promise 实现红绿灯实例代码(demo)
2017/10/20 Javascript
vue v-for循环重复数据无法添加问题解决方法【加track-by='索引'】
2019/03/15 Javascript
webpack的pitching loader详解
2019/09/23 Javascript
ant design中upload组件上传大文件,显示进度条进度的实例
2020/10/29 Javascript
JavaScript实现表单验证功能
2020/12/09 Javascript
[47:12]TFT vs Secret Supermajor小组赛C组 BO3 第三场 6.3
2018/06/04 DOTA
[00:47]DOTA2荣耀之路6:玩不了啦!
2018/05/30 DOTA
linux系统使用python监测网络接口获取网络的输入输出
2014/01/15 Python
python遍历类中所有成员的方法
2015/03/18 Python
初探TensorFLow从文件读取图片的四种方式
2018/02/06 Python
解决Mac下首次安装pycharm无project interpreter的问题
2018/10/29 Python
Python 单元测试(unittest)的使用小结
2018/11/14 Python
python使用递归的方式建立二叉树
2019/07/03 Python
keras用auc做metrics以及早停实例
2020/07/02 Python
python如何操作mysql
2020/08/17 Python
西安启天科技有限公司网络工程师面试题笔试题
2016/06/12 面试题
名人演讲稿范文
2013/12/28 职场文书
酒店总经理助理岗位职责
2014/02/01 职场文书
供用电专业求职信
2014/07/07 职场文书
js Proxy的原理详解
2021/05/25 Javascript
Nginx报错104:Connection reset by peer问题的解决及分析
2022/07/23 Servers