介绍ES6 Generators什么是Generators(生成器函数)?让我们先来看看一个例子 。
function* quips(name) {yield "hello " + name + "!";yield "i hope you are enjoying the blog posts";if (name.startsWith("X")) {yield "it's cool how your name starts with X, " + name;}yield "see you later!";}这是一只会说话的猫的一些代码,可能是当今互联网上最重要的一种应用 。它看起来有点像一个函数,对吗?这被称为生成器-函数,它与函数有很多共同之处 。但你马上就能看到两个不同之处 。
- 普通函数以function开头,生成器函数以function*开头
- 在生成器函数中,yield是一个关键字,语法看起来像return 。不同的是,函数(甚至是生成器函数)只能返回一次,而生成器函数可以“yield”任何次数 。yield表达式暂停了生成器的执行,同时它可以在以后再次恢复 。
> var iter = quips("jorendorff");[object Generator]> iter.next(){ value: "hello jorendorff!", done: false }> iter.next(){ value: "i hope you are enjoying the blog posts", done: false }> iter.next(){ value: "see you later!", done: false }> iter.next(){ value: undefined, done: true }你可能已经非常习惯于普通函数和它们的行为方式 。当你调用它们时,它们会立即开始运行,并一直运行到返回或抛出异常 。所有这些对任何JS程序员来说都是第二天性 。调用一个生成器看起来也是一样的:quips("jorendorff") 。但是当你调用一个生成器时,它还没有开始运行 。相反,它返回一个暂停的Generator对象iter(就是在上面的例子中叫做iter的对象) 。你可以把这个Generator对象看作是一个函数调用,在调用前被冻结 。具体来说,它被冻结在生成器函数的顶端,就在运行其第一行代码之前 。每次你调用Generator对象的方法.next()时,函数调用都会自我解冻,并运行到下一个yield表达式为止 。这就是为什么我们每次调用上面的iter.next()方法,都会得到一个不同的字符串值 。这些都是由函数quips()中的yield表达式产生的值 。在最后一次iter.next()调用中,我们终于到达了生成器-函数的终点,所以结果中.done字段的值是true 。到达生成器函数的终点就像普通函数返回undefined一样,这就是为什么结果的value字段值是undefined 。现在可能是一个好时机,回到会说话的猫的演示页面,真正地玩一玩代码 。试着把yield放在一个循环里面 。会发生什么?从技术上讲,每次Generator执行yield时,它的堆栈--局部变量、参数、临时值以及当前在Generator主体中的执行位置--都会从堆栈中删除 。然而,Generator对象会保留对这个堆栈框架的引用(或副本),以便以后.next()调用可以重新激活它并继续执行 。
值得指出的是,Generator不是线程 。在有线程的语言中,多段代码可以同时运行,通常会导致竞赛条件、非确定性和甜蜜的性能 。Generator则完全不是这样的 。当一个Generator运行时,它与调用者在同一个线程中运行 。执行的顺序是顺序的、确定的,而不是并发的 。与系统线程不同,Generator只在其函数体中标明的yield点上暂停运行 。
好了 。我们知道Generator是什么 。我们已经看到了一个Generator的运行,暂停自己,然后恢复执行 。现在有个大问题 。这种奇怪的能力怎么可能有用?
Generators就是迭代器(Generators are iterators)ES6迭代器不仅仅是一个单一的内置类 。它们是该语言的一个扩展点 。你可以通过实现两个方法Symbol.iterator和next()来创建你自己的迭代器 。但是实现一个接口至少要做一点工作 。让我们看看迭代器的实现在实践中是什么样的 。作为一个例子,让我们做一个简单的迭代器range,它只是从一个数字到另一个数字进行计数,就像一个老式的C循环
for (;;)一样 。// This should "ding" three timesfor (var value of range(0, 3)) {alert("Ding! at floor #" + value);}这里有一个解决方案,使用ES6类class 。class RangeIterator {constructor(start, stop) {this.value = https://tazarkount.com/read/start;this.stop = stop;}[Symbol.iterator]() { return this; }next() {var value = this.value;if (value < this.stop) {this.value++;return {done: false, value: value};} else {return {done: true, value: undefined};}}}// Return a new iterator that counts up from'start' to 'stop'.function range(start, stop) {return new RangeIterator(start, stop);}代码示例这就是在Java或Swift中实现迭代器的情况 。这并不坏 。但也不完全是微不足道的 。这段代码里有什么错误吗?这可不好说 。它看起来完全不像我们在这里试图模仿的原始循环:
for (;;),迭代器协议迫使我们拆除了循环 。在这一点上,你可能对迭代器感到有点冷淡 。它们可能很好用,但似乎很难实现 。你可能不会想建议我们在JS语言中引入一个疯狂的、令人费解的新控制流结构,只是为了使迭代器更容易构建 。但既然我们有生成器Generator,我们能在这里使用它们吗?让我们试试吧 。
function* range(start, stop) {for (var i = start; i < stop; i++)yield i;}代码示例上面的4行range()代码可以直接替代以前的23行实现,包括整个类RangeIterator 。
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
