yield表达式

如果在 Generator函数内部,调用另一个 Generator函数,默认情况下是没有效果的。

  1. function* foo() {
  2. yield 'a';
  3. yield 'b';
  4. }
  5. function* bar() {
  6. yield 'x';
  7. foo();
  8. yield 'y';
  9. }
  10. for (let v of bar()){
  11. console.log(v);
  12. }
  13. // "x"
  14. // "y"

上面代码中,foobar都是 Generator函数,在bar里面调用foo,是不会有效果的。

这个就需要用到yield*表达式,用来在一个 Generator函数里面执行另一个 Generator函数。

  1. function* bar() {
  2. yield 'x';
  3. yield* foo();
  4. yield 'y';
  5. }
  6. // 等同于
  7. function* bar() {
  8. yield 'x';
  9. yield 'a';
  10. yield 'b';
  11. yield 'y';
  12. }
  13. // 等同于
  14. function* bar() {
  15. yield 'x';
  16. for (let v of foo()) {
  17. yield v;
  18. }
  19. yield 'y';
  20. }
  21. for (let v of bar()){
  22. console.log(v);
  23. }
  24. // "x"
  25. // "a"
  26. // "b"
  27. // "y"

再来看一个对比的例子。

  1. function* inner() {
  2. yield 'hello!';
  3. }
  4. function* outer1() {
  5. yield 'open';
  6. yield inner();
  7. yield 'close';
  8. }
  9. var gen = outer1()
  10. gen.next().value // "open"
  11. gen.next().value // 返回一个遍历器对象
  12. gen.next().value // "close"
  13. function* outer2() {
  14. yield 'open'
  15. yield* inner()
  16. yield 'close'
  17. }
  18. var gen = outer2()
  19. gen.next().value // "open"
  20. gen.next().value // "hello!"
  21. gen.next().value // "close"

上面例子中,outer2使用了yield*outer1没使用。结果就是,outer1返回一个遍历器对象,outer2返回该遍历器对象的内部值。

从语法角度看,如果yield表达式后面跟的是一个遍历器对象,需要在yield表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*表达式。

  1. let delegatedIterator = (function* () {
  2. yield 'Hello!';
  3. yield 'Bye!';
  4. }());
  5. let delegatingIterator = (function* () {
  6. yield 'Greetings!';
  7. yield* delegatedIterator;
  8. yield 'Ok, bye.';
  9. }());
  10. for(let value of delegatingIterator) {
  11. console.log(value);
  12. }
  13. // "Greetings!
  14. // "Hello!"
  15. // "Bye!"
  16. // "Ok, bye."

上面代码中,delegatingIterator是代理者,delegatedIterator是被代理者。由于yield* delegatedIterator语句得到的值,是一个遍历器,所以要用星号表示。运行结果就是使用一个遍历器,遍历了多个 Generator函数,有递归的效果。

yield*后面的 Generator函数(没有return语句时),等同于在 Generator函数内部,部署一个for...of循环。

  1. function* concat(iter1, iter2) {
  2. yield* iter1;
  3. yield* iter2;
  4. }
  5. // 等同于
  6. function* concat(iter1, iter2) {
  7. for (var value of iter1) {
  8. yield value;
  9. }
  10. for (var value of iter2) {
  11. yield value;
  12. }
  13. }

上面代码说明,yield*后面的 Generator函数(没有return语句时),不过是for...of的一种简写形式,完全可以用后者替代前者。反之,在有return语句时,则需要用var value = yield* iterator的形式获取return语句的值。

如果yield*后面跟着一个数组,由于数组原生支持遍历器,因此就会遍历数组成员。

  1. function* gen(){
  2. yield* ["a", "b", "c"];
  3. }
  4. gen().next() // { value:"a", done:false }

上面代码中,yield命令后面如果不加星号,返回的是整个数组,加了星号就表示返回的是数组的遍历器对象。

实际上,任何数据结构只要有 Iterator 接口,就可以被yield*遍历。

  1. let read = (function* () {
  2. yield 'hello';
  3. yield* 'hello';
  4. })();
  5. read.next().value // "hello"
  6. read.next().value // "h"

上面代码中,yield表达式返回整个字符串,yield*语句返回单个字符。因为字符串具有 Iterator 接口,所以被yield*遍历。

如果被代理的 Generator函数有return语句,那么就可以向代理它的 Generator函数返回数据。

  1. function *foo() {
  2. yield 2;
  3. yield 3;
  4. return "foo";
  5. }
  6. function *bar() {
  7. yield 1;
  8. var v = yield *foo();
  9. console.log( "v: " + v );
  10. yield 4;
  11. }
  12. var it = bar();
  13. it.next()
  14. // {value: 1, done: false}
  15. it.next()
  16. // {value: 2, done: false}
  17. it.next()
  18. // {value: 3, done: false}
  19. it.next();
  20. // "v: foo"
  21. // {value: 4, done: false}
  22. it.next()
  23. // {value: undefined, done: true}

上面代码在第四次调用next方法的时候,屏幕上会有输出,这是因为函数fooreturn语句,向函数bar提供了返回值。

再看一个例子。

  1. function* genFuncWithReturn() {
  2. yield 'a';
  3. yield 'b';
  4. return 'The result';
  5. }
  6. function* logReturned(genObj) {
  7. let result = yield* genObj;
  8. console.log(result);
  9. }
  10. [...logReturned(genFuncWithReturn())]
  11. // The result
  12. // 值为 [ 'a', 'b' ]

上面代码中,存在两次遍历。第一次是扩展运算符遍历函数logReturned返回的遍历器对象,第二次是yield*语句遍历函数genFuncWithReturn返回的遍历器对象。这两次遍历的效果是叠加的,最终表现为扩展运算符遍历函数genFuncWithReturn返回的遍历器对象。所以,最后的数据表达式得到的值等于[ 'a', 'b' ]。但是,函数genFuncWithReturnreturn语句的返回值The result,会返回给函数logReturned内部的result变量,因此会有终端输出。

yield*命令可以很方便地取出嵌套数组的所有成员。

  1. function* iterTree(tree) {
  2. if (Array.isArray(tree)) {
  3. for(let i=0; i < tree.length; i++) {
  4. yield* iterTree(tree[i]);
  5. }
  6. } else {
  7. yield tree;
  8. }
  9. }
  10. const tree = [ 'a', ['b', 'c'], ['d', 'e'] ];
  11. for(let x of iterTree(tree)) {
  12. console.log(x);
  13. }
  14. // a
  15. // b
  16. // c
  17. // d
  18. // e

下面是一个稍微复杂的例子,使用yield*语句遍历完全二叉树。

  1. // 下面是二叉树的构造函数,
  2. // 三个参数分别是左树、当前节点和右树
  3. function Tree(left, label, right) {
  4. this.left = left;
  5. this.label = label;
  6. this.right = right;
  7. }
  8. // 下面是中序(inorder)遍历函数。
  9. // 由于返回的是一个遍历器,所以要用generator函数。
  10. // 函数体内采用递归算法,所以左树和右树要用yield*遍历
  11. function* inorder(t) {
  12. if (t) {
  13. yield* inorder(t.left);
  14. yield t.label;
  15. yield* inorder(t.right);
  16. }
  17. }
  18. // 下面生成二叉树
  19. function make(array) {
  20. // 判断是否为叶节点
  21. if (array.length == 1) return new Tree(null, array[0], null);
  22. return new Tree(make(array[0]), array[1], make(array[2]));
  23. }
  24. let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
  25. // 遍历二叉树
  26. var result = [];
  27. for (let node of inorder(tree)) {
  28. result.push(node);
  29. }
  30. result
  31. // ['a', 'b', 'c', 'd', 'e', 'f', 'g']

es6 yield表达式相关推荐

  1. python学习第十节(yield表达式的应用+内置函数)

    上节复习 yield表达式 g.send(1) send函数是相当于next并且给yield传一个值,先传值,再next 加上装饰器 yield表达式的应用 第一个是当前目录的地址 第二个是当前目录下 ...

  2. python生成器yield原理_Python generator生成器和yield表达式详解

    前言 Python生成器(generator)并不是一个晦涩难懂的概念.相比于MetaClass和Closure等概念,其较为容易理解和掌握.但相对于程序结构:顺序.循环和分支而言其又不是特别的直观. ...

  3. yield表达式形式的应用

    基本格式 如何给yield传值 yield表达式形式的应用 栗子一吃货函数 栗子二模拟linux中的命令 grep -rl 查找内容 目录 基本格式 x = yield 把yield的接收值,赋值给x ...

  4. es6 Class 表达式

    Class 表达式 与函数一样,ES6 类也可以使用表达式的形式定义. const MyClass = class Me { getClassName() { return Me.name; } }; ...

  5. ES6——generator与yield

    文章目录 一.Generator生成器函数 1. 特点 2. 关联概念 二.yield的加入 1. 引入 2. yield 特点 三.举个例子 一.Generator生成器函数 它是ES6新推出的一种 ...

  6. Python中的yield关键字及表达式、生成器、生成器迭代器、生成器表达式详解

    文章目录 1. yield关键字及表达式.生成器.生成器迭代器.生成器表达式 1.1 yield关键字及表达式(yield expression) 1.1.1 yield关键字 1.1.2 yield ...

  7. es6 generator_让我们探索一下ES6 Generators

    es6 generator by Tiago Lopes Ferreira 由Tiago Lopes Ferreira 让我们探索一下ES6 Generators (Let's explore ES6 ...

  8. 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)

    一.介绍 现在的网络上已经有各样关于 ECMAScript 规范介绍和分析的文章,而我自己重新学习一遍这些规范,整理出这么一份笔记,比较精简,主要内容涵盖ES6.ES7.ES8.ES9,后续会增加面试 ...

  9. ES6简单总结(搭配简单的讲解和小案例)

    在学习es6的过程中,为了方便自己复习,以及查看,对api做了一个极简用例介绍.如有错误多多指正. 一 let和const 1.let (1)一个大括号就是一个块级作用域,let声明的变量只在自己作用 ...

最新文章

  1. 问一下,线程池里面到底该设置多少个线程?
  2. 爬取了1W个字节跳动岗位信息,我发现了什么?
  3. Unity GUI(uGUI)使用心得与性能总结
  4. chrome扩展推荐:有我,你又省了一个ssh客户端 --- Secure Shell
  5. 命令行操作svn和git和git
  6. MapReduce的构思和框架结构
  7. java学习(68):局部内部类
  8. C/C++unlink函数的使用
  9. Linux多线程实践(4) --线程特定数据
  10. 通勤一小时,堵车半小时,AI 救救社畜
  11. tcpdump常用选项
  12. 漏洞分析C#反编译软件Reflector 11.1.0.2167(最新版)(附补丁下载)
  13. 小波分析工具包 matlab,matlab小波工具箱下载|
  14. linux 可道云_阿里云linux+kodexplorer可道云搭建私有云盘-阿里云开发者社区
  15. RPGViewer - 档案文件格式概述
  16. 第二届“马栏山”杯国际音视频算法大赛音乐节拍检测题参赛总结及分享(Rank7)
  17. 看图说话之二叉树的前序,中序,后序,层次遍历方式
  18. win10 更新之后使用win+s 或者 win+Q 出现新闻推荐和热门搜索
  19. 影评系统的机遇和挑战
  20. 【Alpha】Scrum Meeting 5

热门文章

  1. 常用JS正则匹配函数
  2. 10个有关String的面试问题
  3. 网络安装archlinux(2012.8.20)笔记
  4. Java基础-环境变量设置及Java命令行使用
  5. java 注销变量_[ Java学习基础 ] Java对象的创建和销毁
  6. 解决 clipboard.js 在ios中失效的问题
  7. 干货集中营 ReactiveCocoa+RXSwift+MVVM
  8. Linux日常运维(rsync通过服务连接,linux日志,screen)
  9. 【原创】Erlang 之 entop 使用问题
  10. Windows Mobile 6中禁用键盘或者是禁用某些按键(C#)