Generator

常规函数只会返回一个单一值(或者不返回任何值)。

而 Generator 可以按需一个接一个地返回(“yield”)多个值。它们可与 iterable 完美配合使用,从而可以轻松地创建数据流。

Generator 函数

要创建一个 generator,我们需要一个特殊的语法结构:function*,即所谓的 “generator function”。

它看起来像这样:

function* generateSequence() {yield 1;yield 2;return 3;
}

Generator 函数与常规函数的行为不同。在此类函数被调用时,它不会运行其代码。而是返回一个被称为 “generator object” 的特殊对象,来管理执行流程。

我们来看一个例子:

function* generateSequence() {yield 1;yield 2;return 3;
}// "generator function" 创建了一个 "generator object"
let generator = generateSequence();
alert(generator); // [object Generator]

到目前为止,上面这段代码中的 函数体 代码还没有开始执行:

一个 generator 的主要方法就是 next()。当被调用时(译注:指 next() 方法),它会恢复上图所示的运行,执行直到最近的 yield <value> 语句(value 可以被省略,默认为 undefined)。然后函数执行暂停,并将产出的(yielded)值返回到外部代码。
next() 的结果始终是一个具有两个属性的对象:

  • value: 产出的(yielded)的值。
  • done: 如果 generator 函数已执行完成则为 true,否则为 false

例如,我们可以创建一个 generator 并获取其第一个产出的(yielded)值:

function* generateSequence() {yield 1;yield 2;return 3;
}let generator = generateSequence();let one = generator.next();alert(JSON.stringify(one)); // {value: 1, done: false}

截至目前,我们只获得了第一个值,现在函数执行处在第二行:


让我们再次调用 generator.next()。代码恢复执行并返回下一个 yield 的值:

let two = generator.next();alert(JSON.stringify(two)); // {value: 2, done: false}


如果我们第三次调用 generator.next(),代码将会执行到 return 语句,此时就完成这个函数的执行:

let three = generator.next();alert(JSON.stringify(three)); // {value: 3, done: true}


现在 generator 执行完成。我们通过 done:true 可以看出来这一点,并且将 value:3 处理为最终结果。

再对 generator.next() 进行新的调用不再有任何意义。如果我们这样做,它将返回相同的对象:{done: true}

function* f(…)function *f(…)

这两种语法都是对的。

但是通常更倾向于第一种语法,因为星号 * 表示它是一个 generator 函数,它描述的是函数种类而不是名称,因此 * 应该和 function 关键字紧贴一起。


Generator 是可迭代的

当你看到 next() 方法,或许你已经猜到了 generator 是可迭代(iterable)的。(译注:next() 是 iterator 的必要方法)

我们可以使用 for..of 循环遍历它所有的值:

function* generateSequence() {yield 1;yield 2;return 3;
}let generator = generateSequence();for(let value of generator) {alert(value); // 1,然后是 2
}

for..of 写法是不是看起来比 .next().value 优雅多了?

……但是请注意:上面这个例子会先显示 1,然后是 2,然后就没了。它不会显示 3

这是因为当 done: true 时,for..of 循环会忽略最后一个 value。因此,如果我们想要通过 for..of 循环显示所有的结果,我们必须使用 yield 返回它们:

function* generateSequence() {yield 1;yield 2;
yield 3;
}let generator = generateSequence();for(let value of generator) {alert(value); // 1,然后是 2,然后是 3
}

因为 generator 是可迭代的,我们可以使用 iterator 的所有相关功能,例如:spread 语法 ...

function* generateSequence() {yield 1;yield 2;yield 3;
}let sequence = [0, ...generateSequence()];alert(sequence); // 0, 1, 2, 3

在上面这段代码中,...generateSequence() 将可迭代的 generator 对象转换为了一个数组


使用 generator 进行迭代

在Iterable object(可迭代对象) 中,我们可以创建一个可迭代的 range 对象,它返回 from..to 的值。

let range = {from: 1,to: 5,// for..of range 在一开始就调用一次这个方法[Symbol.iterator]() {// ...它返回 iterator object:// 后续的操作中,for..of 将只针对这个对象,并使用 next() 向它请求下一个值return {current: this.from,last: this.to,// for..of 循环在每次迭代时都会调用 next()next() {// 它应该以对象 {done:.., value :...} 的形式返回值if (this.current <= this.last) {return { done: false, value: this.current++ };} else {return { done: true };}}};}
};// 迭代整个 range 对象,返回从 `range.from` 到 `range.to` 范围的所有数字
alert([...range]); // 1,2,3,4,5

我们可以通过提供一个 generator 函数作为 Symbol.iterator,来使用 generator 进行迭代:

下面是一个相同的 range,但紧凑得多:

let range = {from: 1,to: 5,*[Symbol.iterator]() { // [Symbol.iterator]: function*() 的简写形式for(let value = this.from; value <= this.to; value++) {yield value;}}
};alert( [...range] ); // 1,2,3,4,5

之所以代码正常工作,是因为 range[Symbol.iterator]() 现在返回一个 generator,而 generator 方法正是 for..of 所期望的:

  • 它具有 .next() 方法
  • 它以 {value: ..., done: true/false} 的形式返回值

当然,这不是巧合。Generator 被添加到 JavaScript 语言中是有对 iterator 的考量的,以便更容易地实现 iterator。

带有 generator 的变体比原来的 range 迭代代码简洁得多,并且保持了相同的功能。

Generator 可以永远产出(yield)值

在上面的示例中,我们生成了有限序列,但是我们也可以创建一个生成无限序列的 generator,它可以一直产出(yield)值。例如,无序的伪随机数序列。

这种情况下肯定需要在 generator 的 for..of 循环中添加一个 break(或者 return)。否则循环将永远重复下去并挂起。


Js的Generator函数(一)相关推荐

  1. js中Generator函数详解

    文章目录 1. Generator的定义和执行 2. Generator中yield在赋值号左边的情况 3. Generator函数嵌套使用 4. 使用generator函数完成网络请求 1. Gen ...

  2. JS中Generator函数的详解

    概念     Generator 函数是 ES6 提供的一种异步编程解决方案.它既是一个生成器,也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器 Iterator 对象,我们可以通过这个迭 ...

  3. js中的yield、yield*和Generator函数

    基本概念 yield和yield*都是js中的关键字,他们不能直接使用:只能配合Generator进行使用:Generator是一种函数,声明方式和普通函数类似,只不过要在function后面加个*( ...

  4. JS中的Generator函数

    Generator 函数是 ES6 提供的一种异步编程解决方案,它是一个状态机,封装了多个内部状态:它提供一种有效的方式来制作迭代器,并且能够处理无限数据流:当与Promises一起使用时,生成器可以 ...

  5. 【ES6】Generator函数详解

    [ES6]Generator函数详解 一.Generator函数简介 基本概念 函数写法 yield关键字介绍 二.next方法的参数 三.for...of循环 四.关于普通throw()与Gener ...

  6. es6 中的generator函数控制流程

    Generator函数跟普通函数的写法有非常大的区别: 一是,function关键字与函数名之间有一个星号: 二是,函数体内部使用yield语句,定义不同的内部状态(yield在英语里的意思就是&qu ...

  7. JS 总结之函数、作用域链

    在 JavaScript 中,函数实际上是一个对象. ? 声明 JavaScript 用 function 关键字来声明一个函数: function fn () {} 复制代码 变体:函数表达式: v ...

  8. Generator 函数的含义与用法

    异步编程对 JavaScript 语言太重要.JavaScript 只有一根线程,如果没有异步编程,根本没法用,非卡死不可. 以前,异步编程的方法,大概有下面四种. 回调函数 事件监听 发布/订阅 P ...

  9. 对于es6的小小理解之generator函数

    相信很多学js的人都看过es6,我也是最近才看的es6标准.下面我来说一下我对es6笼统的看法,如有不对欢迎评论交流. js有很长很长的历史,大家应该都有了解过.es6是15年发布的版本,由TC39主 ...

最新文章

  1. 从今天开始,自己做SEO。
  2. OGG 同步报错 - TCP/IP error 111 (Connection refused)
  3. 读书笔记:《Aspx开发200问》——如何实现Repeater控件的分页
  4. java jtable 添加数据库_java-将jTable中的数据插入数据库
  5. leetcode算法题--最长等差数列★
  6. android键盘ui,android – 在自定义键盘中重新调整候选视图的UI
  7. 人脸识别撞脸名画_与名画“撞脸”火爆数博会 观众直呼“太好玩”【高清组图】...
  8. 0002-Add Two Numbers(两数相加)
  9. Ubuntu 完全卸载 Apache2
  10. python自动化_day4_迭代器生成器内置函数和匿名函数
  11. linux修改私钥的密码,linux使用密钥+密码登录ssh(centos7)
  12. 基于分段解析法的单自由度反应谱程序
  13. python有趣的简单代码-python有趣代码
  14. php日记源码,留言日记 - PHP源码 - 源码下载
  15. CentOS7修改主机名称(hostname)总是不成功的原因及解决办法
  16. 最好用的启动管理软件推荐-点评-对比-分析-下载:
  17. 苹果电脑安装双系统Mac和Win7,详细教程
  18. CMOS基础知识(一)
  19. 【鼠标移入暂停animation动画】
  20. sublime Text3去除文本重复行

热门文章

  1. 脑卒中css评分是什么意思,你知道房颤卒中新评分—ABC评分量表吗?
  2. AE教程丨1分钟学会制作信号故障风特效
  3. 什么是DevOps模式
  4. 升级联想E450(加内存条换固态重装系统win10)
  5. 华为服务器故障灯不开机_华为服务器
  6. 喝java茶,我为你泡一杯花茶
  7. js使用eval解析json(js中使用json)
  8. 什么是IaaS、PaaS和SaaS
  9. 【uoj#311】[UNR #2]积劳成疾 dp
  10. linux系统下修改hosts文件的权限