概念

    Generator 函数是 ES6 提供的一种异步编程解决方案。它既是一个生成器,也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器 Iterator 对象,我们可以通过这个迭代器,手动地遍历相关的值、状态,保证正确的执行顺序。

特征

  1. function 关键字与函数名之间有一个星号(ES6 没有规定,function 关键字与函数名之间的星号,写在哪个位置)
  2. 函数体内部使用 yield 表达式,定义不同的内部状态
  3. 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)
function* gen() { yield 1yield 2return 3yield 4
}let g = gen();
console.log(g.next())   // {value: 1, done: false}
console.log(g.next())   // {value: 2, done: false}
console.log(g.next())   // {value: 3, done: true}
console.log(g.next())   // {value: undefined, done: true}

    每次调用 next() 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式(或 return 语句)为止。换言之,Generator 函数是分段执行的,yield 表达式是暂停执行的标记,而 next() 方法可以恢复执行。
    调用 Generator 函数,返回一个遍历器对象 (Iterator),代表 Generator 函数的内部指针。每次调用遍历器对象的 next() 方法,就会返回一个有着 value 和 done 两个属性的对象。value 属性表示当前的内部状态的值,是 yield 表达式后面那个表达式的值;done 属性是一个布尔值,表示是否遍历结束。
    Generator 函数的暂停执行的效果,意味着可以把异步操作写在 yield 语句里面,等到调用 next 方法时再往后执行。这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在 yield 语句下面,反正要等到调用 next() 方法时再执行。所以,Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数。

    注意:如果 return 语句后面还有 yield 表达式,那么后面的 yield 完全不生效

Iterator 的 return 的值不会被 for…of 循环到 , 也不会被扩展符遍历到

function* gen() { yield 1yield 2return 3
}let g = gen()console.log([...g])     // [1, 2]for(let foo of g) {console.log( foo )  // 1, 2
}

yield 表达式和 next() 方法

    由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield 表达式就是暂停标志。

遍历器对象的next方法的运行逻辑:

  1. 遇到 yield 表达式,就暂停执行后面的操作,并将紧跟在 yield 后面的那个表达式的值,作为返回的对象的 value 属性值
  2. 下一次调用 next() 方法时,再继续往下执行,直到遇到下一个 yield 表达式
  3. 如果没有再遇到新的 yield 表达式,就一直运行到函数结束,直到 return 语句为止,并将 return 语句后面的表达式的值,作为返回的对象的 value 属性值
  4. 如果该函数没有 return 语句,则返回的对象的 value 属性值为 undefined

注意:

  1. yield 表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行
  2. yield 语句只能用于 function* 的作用域,如果 function* 的内部还定义了其他的普通函数,则函数内部不允许使用 yield 语句
  3. yield 语句如果参与运算,必须用括号括起来
function* gen() {yield  123 + 456;      // yield后面的表达式123 + 456,不会立即求值,只会在next方法将指针移到这一句时,才会求值
}function* gen() {return function () {yield 1   // SyntaxError}
}function* gen() {console.log('Hello' + yield); // SyntaxErrorconsole.log('Hello' + (yield)); // OK
}

next() 方法的参数

  • yield 表达式本身没有返回值,或者说总是返回undefined
  • next() 方法可以带一个参数,该参数会改变上一个yield表达式的返回值
function* gen(x) {var y = 2 * (yield (x + 1))var z = yield (y / 3)return (x + y + z)
}var a = gen(5);
a.next()    // {value: 6, done: false}
a.next()    // {value: NaN, done: false}
a.next()    // {value: NaN, done: true}var b = gen(5);
b.next()    // {value: 6, done: false}
b.next(12)  // {value: 8, done: false}
b.next(13)  // {value: 42, done: true}

    第一次调用 a 的 next() 方法时,返回 x+1 的值6。第二次运行a的 next() 方法的时候不带参数,导致 y 的值等于2 * undefined(即NaN),除以 3 以后还是NaN,因此返回对象的 value 属性也等于 NaN。第三次运行a的 next() 方法的时候不带参数,所以 z 等于undefined,返回对象的 value 属性等于5 + NaN + undefined,即NaN。
     第一次调用b的 next() 方法时,返回 x+1 的值6;第二次调用 next() 方法,将上一次 yield 表达式的值设为12,因此y等于24,返回y / 3的值8;第三次调用 next() 方法,将上一次 yield 表达式的值设为13,因此 z 等于13,这时 x 等于5,y 等于24,所以 return 语句的值等于42。

注意:

  1. 在第一次使用next方法时,传递参数是无效的
  2. V8 引擎直接忽略第一次使用 next() 方法时的参数,只有从第二次使用next方法开始,参数才是有效的。
  3. 从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。

Generator 的应用

1. 斐波那契数列

function* flb(){let [pre, cur] = [0, 1]for(;;) {[pre, cur] = [cur, pre + cur]yield cur}
}
for(let k of flb()){if( k > 1000 ) breakconsole.log(k)
}// 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

2. Genarator 对数组部署 Iterator 接口

function* deploy(arr){var Index = 0while(Index < arr.length){yield arr[Index++]}
}var arr = ['name', 'age', 'sex', 'hobby']var val = deploy(arr)
console.log(val.next())  // {value: "name", done: false}
console.log(val.next())  // {value: "age", done: false}
console.log(val.next())  // {value: "sex", done: false}
console.log(val.next())  // {value: "hobby", done: false}
console.log(val.next())  // {value: undefined, done: true}for(let k of deploy(arr)) {console.log(k)  // name age sex hobby
}

参考文献 http://es6.ruanyifeng.com/#docs/generator

JS中Generator函数的详解相关推荐

  1. JS中的event 对象详解

    JS中的event 对象详解 JS的event对象 Event属性和方法: 1. type:事件的类型,如onlick中的click: 2. srcElement/target:事件源,就是发生事件的 ...

  2. JS 中 valueOf() 方法的详解

    JS 中 valueOf() 方法的详解 JavaScript 中的 valueOf() 方法用于返回指定对象的原始值,若对象没有原始值,则将返回对象本身.通常由JavaScript内部调用,而不是在 ...

  3. C++中substr()函数用法详解

    C++中substr()函数用法详解 原型: string substr (size_t pos = 0, size_t len = npos) const; 返回一个新构造的string对象,其值初 ...

  4. c++ memset 语言_C++中memset函数用法详解

    本文实例讲述了C++中memset函数用法.分享给大家供大家参考,具体如下: 功 能: 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,块的大小由第三个参数指定,这个函数通常 ...

  5. php dump函数详解,php中var_dump()函数的详解说明

    本文章给大家全面的介绍一下关于php中var_dump()函数用法详解,大家可参考参考. var_dump()void var_dump ( mixed expression [, mixed exp ...

  6. js中indexOf的用法详解

    js中indexOf的用法详解 String.IndexOf 方法 (Char, [startIndex], [count]) 报告指定字符在此实例中的第一个匹配项的索引.搜索从指定字符位置开始,并检 ...

  7. C语言结构体中定义函数指针详解

    C语言结构体中定义函数指针详解 结构体指针函数应用场景之一--驱动程序编写 结构体的一些基本用法 形式1:先定义结构体类型,再定义变量 形式2:在定义类型的同时定义变量 形式3:直接定义变量,用无名结 ...

  8. C++中发声函数Beep详解

    一.目的 1.想知道Beep是什么 二.参考 1.C++中发声函数Beep详解 https://blog.csdn.net/v1t1p9hvbd/article/details/71523218 ①总 ...

  9. python中setattr()函数用法详解

    setattr() 函数对应函数 getattr(),用于设置属性值,该属性不一定是存在的. getattr()用法详见博文:python中getattr()函数用法详解_IT之一小佬的博客-CSDN ...

最新文章

  1. R语言使用ggplot2包使用geom_violin函数绘制分组小提琴图(配置填充色)实战
  2. 20145234黄斐《Java程序设计》第五周
  3. linux truss strace ltrace 对比 诊断调试程序
  4. meanpool maxpool 前向和反向传播
  5. 区分docker stack/service/task
  6. 《计算机视觉:模型、学习和推理》一3.6 正态逆伽马分布
  7. 看完这篇文章保你面试稳操胜券——Vue篇
  8. 查询成绩小于85且是计算机的一项应用,查询练习2
  9. python 消息队列 get是从队首还是队尾取东西_从零开始Python对redis作为消息队列的使用...
  10. oracle存储过程 取时间格式,Oracle存储过程获取YYYY-MM-DD的时间格式
  11. 在RedHat Enterprise Linux 上Oracle 9i的安装配置与调优
  12. 机器学习面试-其他重要算法
  13. poj3204Ikki's Story I - Road Reconstruction(最大流求割边)
  14. java 反射 框架_Java——利用反射实现框架类
  15. Selenium官网教程
  16. 耳挂式蓝牙耳机原理_一种耳挂式蓝牙耳机的制作方法
  17. MySQL(InnoDB剖析):08---InnoDB关键特性(插入缓冲(Insert Buffer)、两次写(doublewrite)、自适应哈希索引(AHI)、异步IO(AIO)、刷新邻接页)
  18. 解决win10更新,笔记本连手机热点网页部分图片、百度网盘验证码显示不出来问题。
  19. V4.0系列软件如何替换授权文件
  20. 餐厅点餐系统需求分析

热门文章

  1. 编程小知识:文件扩展名的作用是什么?通俗易懂的文件扩展名详解
  2. java+lame实现wav到mp3的转换
  3. ssm+JSP计算机毕业设计高校疫情日报管理信息系统abk6n【源码、程序、数据库、部署】
  4. Eyeshot Fem 2022.436 Crack
  5. 在php中加css_如何使用php脚本给html中引用的js和css路径打上版本号
  6. android studio TCP客户端通讯
  7. Android RIL学习
  8. Only no-arg methods may be annotated with @Scheduled
  9. open3d学习教程1--点云对象PointCloud
  10. 论文笔记:Blind Super-Resolution With Iterative Kernel Correction