介绍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表达式暂停了生成器的执行,同时它可以在以后再次恢复。
Generators可以做什么
当你调用生成器-函数quips()时会发生什么?
> 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 = 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。就是因为Generator是迭代器,所以这一切才是可能的。所有的生成器都有一个内置的next()和Symbol.iterator的实现。 你只需写出循环的行为。
在没有Generator的情况下实现迭代器,就像被迫完全用被动语态来写一封长邮件。当简单地说出你的意思不是一个选项时,你最终说的东西可能会变得相当复杂。"我的意思是,我的意思是,我必须在不使用循环语法的情况下描述一个循环的功能,所以RangeIterator又长又奇怪。而Generator就是答案。
我们还可以如何利用生成器作为迭代器的能力呢?
- 让任何对象都可以迭代。只需写一个Generator函数来遍历this,在遍历时产生(yield)每个值。然后把这个生成器函数设置为this对象的[Symbol.iterator]方法。
- 简化建数组函数。假设你有一个函数,每次调用都会返回一个数组的结果,就像下面这个函数:
//将一维数组'图标'切分成长度为'rowLength'的数组function splitIntoRows(icons, rowLength) { var rows = []; for (var i = 0; i < icons.length; i += rowLength) { rows.push(icons.slice(i, i + rowLength)); } return rows;}使用Generator会让这种代码更短一些。
function* splitIntoRows(icons, rowLength) { for (var i = 0; i < icons.length; i += rowLength) { yield icons.slice(i, i + rowLength); }}执行时唯一区别是,它不是一次性计算所有的结果并返回一个数组,而是返回一个迭代器,然后根据需要逐个计算结果。
异常大小的结果。你不可能建立一个无限的数组。但是你可以返回一个Generator,生成一个无尽的序列,每个调用者可以从其中提取他们需要的任何数量的值。
重构复杂的循环。你有一个巨大的丑陋的函数吗?你想把它分解成两个更简单的部分吗?Generator是添加到你的重构工具箱中的一把新刀。当你面对一个复杂的循环时,你可以把代码中产生数据的部分分解出来,把它变成一个单独的生成器-函数。然后将循环改为:for (var data of myNewGenerator(args))
处理可迭代数据的工具。ES6并没有提供一个扩展库,用于过滤、映射,以及一般情况下对任意的可迭代数据集进行任意的处理。但是Generator对于构建你所需要的工具来说是非常棒的,只需要几行代码。例如,假设你需要一个新的在DOM NodeLists上遍历的方法,而不仅仅是Arrays。......
原文转载:http://www.shaoqun.com/a/851364.html
跨境电商:https://www.ikjzd.com/
笨鸟转运:https://www.ikjzd.com/w/1550
sca:https://www.ikjzd.com/w/2424
c88:https://www.ikjzd.com/w/1017.html
介绍ES6Generators什么是Generators(生成器函数)?让我们先来看看一个例子。function*quips(name){yield"hello"+name+"!";yield"ihopeyouareenjoyingtheblogposts";if(name.startsWith("X")){yield
Shopee一店通上线! 1个店铺直卖全东南亚, 首月还免运费!:https://www.ikjzd.com/articles/100884
为什么选择亚马逊FBA?亚马逊FBA运送流程是怎样的?:https://www.ikjzd.com/articles/100885
如何提升秒杀效果?亚马逊运营技巧分享!:https://www.ikjzd.com/articles/100886
亚马逊为什么默认分仓?亚马逊FBA被分仓处理技巧:https://www.ikjzd.com/articles/100887
被两个学长带到宿舍 学长和我在卫生间里做:http://lady.shaoqun.com/a/247424.html
第一次和男朋友合体 做羞羞事做完男朋友拔不出来:http://lady.shaoqun.com/a/248333.html
老师抬起臀部让我进去 掀开老师湿润的短裙:http://lady.shaoqun.com/m/a/248339.html
玉米地里的大嫂让我收不住欲望(4/4):http://lady.shaoqun.com/m/a/82887.html
不再由贝索斯掌舵 亚马逊将驶向何方?:https://www.ikjzd.com/articles/146429
独立日0单?亚马逊订单大跌,大卖7000万库存待清理:https://www.ikjzd.com/articles/146428
词缀en的常见意义和用法:http://lady.shaoqun.com/a/405834.html
2021深圳锦绣中华民俗村泼水节夜场攻略:http://www.30bags.com/a/476661.html
没有评论:
发表评论