摘要:在生成器中使用语句生成器也是函数,所以它也可以使用语句。只是由于生成器本身的特性,其内部的的行为会和一般函数有些差别。
前面2篇系列文章讲解了迭代器和生成器的最常用,最基础的用法;这篇来讨论迭代器和生成器的一些稍稍高级一点的用法:
1: 给迭代器的next()方法传参 2: 从迭代器中抛出错误 3: 在生成器中使用return语句 4: 委托生成器(组合生成器或者生成器组合?)
1: 给迭代器的next()方法传参
在前面2篇系列文章中我们使用的next()方法都是没有传参的,调用next()会依次返回迭代器里面的值。但是,实际上我们是可以给next()方法传参数的,那在这种情况下我们会得到什么样的结果呢?
function* createIterator() { let first = yield 1; console.log(`first: ${first}`); let second = yield first + 2; console.log(`second: ${second}`); let third = yield second + 3; } let iterator = createIterator(); console.log(iterator.next(0)); console.log(iterator.next(4)); console.log(iterator.next(5)); console.log(iterator.next());
我们得到下面的输出结果:
{value: 1, done: false} first: 4 {value: 6, done: false} second: 5 {value: 8, done: false} {value: undefined, done: true}
在第二次和第三次的next调用中我们分别传入参数4和5,而4和5也分别被赋值给了变量first和second。当我们执行:
iterator.next(4)的时候,生成器内部执行的代码实际上是:
let first = 4; yield 4 + 2; //所以我们得到 {value: 6, done: false}
iterator.next(5)的时候,生成器内部执行的代码实际上是:
let second = 5; yield 5 + 3; //所以我们得到 {value: 8, done: false}
看下面一个图或许能更直观一些:
以上截图来自书 Understanding ECMAScript 6
但是上面的例子中,我们也看到一个有趣的现象,就是我们第一次调用next的时候,是传了参数0的iterator.next(0),但是我们依然得到了结果{value: 1, done: false}。这是因为第一次调用next(),无论传递什么参数总是会被丢弃,所以给第一次调用的next()方法传值是无意义的。
或许你看到这里也还不是完全明白了给next()传参时,生成器内部到底是怎样工作。接下来我们再看一个例子,这一次我们在第3次调用next()的时候,不传参数,看会发生什么:
function* createIterator() { let first = yield 1; console.log(`first: ${first}`); let second = yield first + 2; console.log(`second: ${second}`); let third = yield second + 3; } let iterator = createIterator(); console.log(iterator.next(0)); console.log(iterator.next(4)); console.log(iterator.next()); console.log(iterator.next());
我们得到的输出结果是:
{value: 1, done: false} first: 4 {value: 6, done: false} second: undefined {value: NaN, done: false} {value: undefined, done: true}
在第三次的next调用中,我们并没有传递任何参数,生成器内部的执行情况就是:
let second; yield undefined + 2; //所以我们得到结果{value: NaN, done: false}
其实从这里例子我们也可以看出,在生成器内部,yield执行的结果并不会被保存下来赋值给内部的变量,例如这里我们在第三次没有给next()传递参数,那么second的值就是undefined,而不是第二次yield执行结果的value 6。
2: 从迭代器中抛出错误
我们知道一般的函数的执行结果有2种:
1: 返回一个值 2: 抛出一个错误
生成器函数作为一种特殊的函数,但是它本身也是函数,所以它也可以抛出错误。只是它抛出错误的时间与一般函数不同,看一下下面的代码:
function* createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); //{value: 1, done: false} console.log(iterator.next(4)); //{value: 6, done: false} console.log(iterator.throw(new Error("Boom"))); //Uncaught Error: Boom
在生成器内部代码执行情况如下图所示:
以上截图来自书 Understanding ECMAScript 6
当我们抛出错误之后,代码就停止了。let second语句并不会被执行到。
但是生成器里面的throw()它会像yield一样,也会返回一个结果。我们可以像一般函数一样catch这个错误,并且之后的代码依然可以得到执行:
function* createIterator() { let first = yield 1; let second; try { second = yield first + 2; } catch (error) { second = 6; } yield second + 3; } let iterator = createIterator(); console.log(iterator.next()); //{value: 1, done: false} console.log(iterator.next(4)); //{value: 6, done: false} console.log(iterator.throw(new Error("Boom"))); //{value: 9, done: false} console.log(iterator.next());//{value: undefined, done: true}
在这个例子中,我们catch了错误之后,给second赋值6,它后面的代码yield second + 3;也依然可以得到执行。
3: 在生成器中使用return语句
生成器也是函数,所以它也可以使用return语句。只是由于生成器本身的特性,其内部的return的行为会和一般函数有些差别。我们先来看两个例子,就能从中窥探一二:
例1:
function* createIterator() { yield 1; return; yield 2; } let iterator = createIterator(); console.log(iterator.next()); //{value: 1, done: false} console.log(iterator.next()); //{value: undefined, done: true} console.log(iterator.next());//{value: undefined, done: true}
例2:
function* createIterator() { yield 1; return 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); //{value: 1, done: false} console.log(iterator.next()); //{value: 2, done: true} console.log(iterator.next()); //{value: undefined, done: true}
上面的例1只是使用了return语句,然后后面没有跟任何值,例2代码就return了一个数字,我们从调用next()的结果可以看到:
1: return语句会终止返回迭代器里面本可以再迭代的值,会把done设置为false 2: return语句如果指定一个值,那么此次结果的value会被赋为此值;如果没有指定,那value为undefined
4: 委托生成器(组合生成器或者生成器组合?)
单个的生成器函数里,yield后面往往跟一些我们常用的数据类型;但是,我们也可以yield 一个生成器函数,这样的操作就叫委托生成器。先看一个代码的例子:
function *createNumberIterator() { yield 1; yield 2; } function *createColorIterator() { yield "red"; yield "yellow"; } function *createCombinedIterator() { yield *createNumberIterator(); yield *createColorIterator(); } let combinedIterator = createCombinedIterator(); console.log(combinedIterator.next());//{value: 1, done: false} console.log(combinedIterator.next());//{value: 2, done: false} console.log(combinedIterator.next());//{value: "red", done: false} console.log(combinedIterator.next());//{value: "yellow", done: false} console.log(combinedIterator.next());//{value: undefined, done: true}
上面的这个示例,我们创建两个不同的生成器函数createNumberIterator()和createColorIterator(),之后在createCombinedIterator()函数里通过yield语句调用前面的2个生成器函数,这样createCombinedIterator()就成了一个拥有以上2个生成器的迭代器的生成器,调用*createCombinedIterator()的next(),就跟一般的生成器的next()方法的行为一样。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/110287.html
摘要:我个人认为迭代器和生成器是新增的特性里面,非常重要的部分,充分地掌握和使用迭代器和生成器,是十分必要和重要的,所以我会写关于二者的一系列文章。 我个人认为迭代器和生成器是es6新增的特性里面,非常重要的部分,充分地掌握和使用迭代器和生成器,是十分必要和重要的,所以我会写关于二者的一系列文章。话不多说,先来了解一下基本概念:一:什么是迭代器 1: 迭代器是一个对象 2: 迭代器有一个属性...
摘要:迭代器和生成器将迭代的概念直接带入核心语言,并提供一种机制来自定义循环的行为。本文主要会介绍中新增的迭代器和生成器。属性本身是函数,是当前数据结构默认的迭代器生成函数。 本文是 重温基础 系列文章的第十三篇。今日感受:每次自我年终总结,都会有各种情绪和收获。 系列目录: 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理) 【重温基础】1.语法和数据类型 【重温基础】2.流...
摘要:通过生成器创建的迭代器也是可迭代对象,因为生成器默认会为属性赋值。我们可以用来访问对象的默认迭代器,例如对于一个数组获得了数组这个可迭代对象的默认迭代器,并操作它遍历了数组中的元素。 ES6 新的数组方法、集合、for-of 循环、展开运算符(...)甚至异步编程都依赖于迭代器(Iterator )实现。本文会详解 ES6 的迭代器与生成器,并进一步挖掘可迭代对象的内部原理与使用方法 ...
摘要:迭代器是一种特殊对象,每一个迭代器对象都有一个,该方法返回一个对象,包括和属性。默认情况下定义的对象是不可迭代的,但是可以通过创建迭代器。在迭代器中抛出错误不再执行生成器返回语句生成器中添加表示退出操作。迭代器是一个对象。 迭代器(Iterator) ES5实现迭代器 迭代器是什么?遇到这种新的概念,莫慌张。 迭代器是一种特殊对象,每一个迭代器对象都有一个next(),该方法返回一个对...
阅读 1564·2021-10-25 09:44
阅读 2925·2021-09-04 16:48
阅读 1543·2019-08-30 15:44
阅读 2474·2019-08-30 15:44
阅读 1731·2019-08-30 15:44
阅读 2815·2019-08-30 14:14
阅读 2963·2019-08-30 13:00
阅读 2143·2019-08-30 11:09