javascript解释器,javascript解析html
JavaScript Generators指南最近为了更好的理解Redux Sagas的工作原理,重新学习了JavaScript生成器的知识。我把从网上搜集的各种知识点浓缩成一篇文章。我希望这篇文章既容易理解,又足够严谨,可以作为发电机的初学者指南。
简介
JavaScript在ES6中引入了生成器。生成器类似于常规函数,只是它们可以暂停和恢复。生成器也与迭代器密切相关,因为生成器对象是迭代器。
在JavaScript中,函数通常不能在被调用后暂停或停止。(是的,异步函数在等待await语句时会暂停,但异步函数只在ES7中引入。此外,异步函数建立在生成器上。)普通函数只有在返回或抛出错误时才会结束。
函数foo() {
console . log(“Starting”);
const x=42
console . log(x);
console.log(有本事阻止我);
console.log(但你不能);
}相反,生成器允许我们在任何断点暂停执行,并从同一个断点继续执行。
:来自MDN的
生成器和迭代器
:
因此,迭代器的本质是:
由序列定义的对象有一个next()方法…它返回一个具有两个属性的对象:value和done。创建迭代器需要生成器吗?不。事实上,我们已经可以使用ES6之前的闭包来创建一个无限斐波那契数列,如下例所示:
var fibonacci={
下一步:(function () {
var pre=0,cur=1;
返回函数(){
tmp=pre
pre=cur
cur=tmp
返回cur
};
})()
};
Fibonacci . next();//1
Fibonacci . next();//2
Fibonacci . next();//3
Fibonacci . next();//5
Fibonacci . next();//8关于发电机的好处,我再引用一下MDN:
语法
生成器函数使用function *语法创建,并使用yield关键字暂停。
最初调用生成器函数不会执行它的任何代码;相反,它返回一个生成器对象。这个值通过调用生成器的next()方法来使用,该方法执行代码直到遇到yield关键字,然后暂停直到再次调用next()为止。
函数* makeGen() {
屈服‘你好’;
产出‘世界’;
}
const g=makeGen();//g是一个生成器
g . next();//{ value:你好,done: false }
g . next();//{ value: World ,done: false }
g . next();//{value: undefined,done: true}在上面最后一条语句后重复调用g.next()只会返回(或者更准确地说,产生)同一个返回对象:{value: undefined,done: true}。
yield暂停执行
您可能会注意到上面的代码片段有一些特殊的功能。第二个next()调用生成一个对象,该对象的属性是done: false而不是done: true。
既然执行的是生成器函数中的最后一条语句,那么done属性不应该为true吗?不是的。当遇到yield语句时,它后面的值(本例中为“World”)被生成,执行被挂起。因此,第二个next()调用在第二个yield语句上被挂起,所以执行还没有完成—只有当执行在第二个yield语句后重新开始时,执行才完成(即done: true),代码不再运行。
我们可以认为next()调用是告诉程序运行到下一个yield语句(假设存在),生成一个值,然后暂停。程序在恢复执行之前不会知道yield语句后面什么都没有,只能通过另一个next()调用来恢复执行。
yield和return
在上面的示例中,我们使用yield将值传递到生成器外部。我们也可以使用return(就像在普通函数中一样);但是,使用return终止执行,并设置done: true。
函数* makeGen() {
屈服‘你好’;
回‘拜拜’;
产出‘世界’;
}
const g=makeGen();//g是一个生成器
g . next();//{ value:你好,done: false }
g . next();//{ value: Bye ,done: true }
g . next();//{value: undefined,done: true}因为在return语句上不暂停执行,而且根据定义,在return语句之后不能执行任何代码,所以done设置为true。
yield:next方法的参数
到目前为止,我们一直使用yield在生成器外部传递值(并暂停其执行)。
然而,yield实际上是双向的,它允许我们将值传递给生成器函数。
函数* makeGen() {
const foo=yield‘Hello world’;
console . log(foo);
}
const g=makeGen();
g . next(1);//{ value: Hello world ,done: false }
g . next(2);//logs2,yields {value: undefined,done: true}等一下。应该不是 1 打印到控制台,而是控制台打印出 2 ?一开始我发现这部分在概念上是反直觉的,因为我的预期赋值foo=1。毕竟,我们将“1”传递给next()方法调用,从而生成Hello world,对吗?
但事实并非如此。传递给第一个next的值(.)呼叫将被丢弃。除了这好像是ES6的规范,其实没有别的原因。从语义上来说,第一个next方法是用来启动遍历对象的,所以不用带参数。
我喜欢这样合理化程序的执行:
在第一次next()调用时,会一直运行到遇到yield Hello world ,在此基础上生成{value: Hello World ,done: false}并暂停。事情就是这样。如您所见,传递给第一个next()调用的任何值都不会被使用(因此它将被丢弃)。下次什么时候(.)时,将继续执行。在这种情况下,执行需要给常量foo赋值(由yield语句决定)。因此,我们对next(2)的第二次调用被赋值为foo=2。程序不会在这里停止,它会一直运行,直到遇到下一个yield或return语句。在这个例子中,没有更多的产出,所以它记录2并返回undefined done: true。在生成器中使用异步因为yield是一个双向通道,允许信息双向流动,它允许我们以一种非常酷的方式使用生成器。到目前为止,我们主要使用yield在生成器外部传递值。但是,我们也可以利用yield的双向特性来同步编写异步函数。使用上述概念,我们可以创建一个类似于同步代码但实际上执行异步功能的基本函数:
功能请求(url) {
获取(url)。然后(res={
it . next(RES);//恢复迭代器执行
});
}
function * main() {
const rawResponse=yield请求( https://some-URL . com );
const return value=synchronouslyProcess(raw response);
控制台. log(return value);
}
const it=main();
it . next();//记住,第一个next()调用不接受输入,这是它的工作方式。首先,我们声明一个请求函数和一个主生成器函数。接下来,通过调用main()创建一个迭代器it。然后,我们首先调用它. next()。
在function * main()的第一行中,yield request( https://some-URL . com )之后执行暂停。Request()隐式返回undefined,所以我们实际上生成了undefined值,但这无关紧要—我们没有使用该值。
当request()函数中的fetch()调用完成时,将调用it.next(res)并执行以下两项操作:
它继续执行;和
将itres传递给分配给rawResponse的生成器函数。
最后,main()的其余部分将同步完成。
这是一个非常基本的设置,应该与promise有一些相似之处。有关yield和异步的更详细介绍,请参考本文。
生成器是一次性
我们不能重用生成器,但是我们可以从生成器函数创建新的生成器。
函数* makeGen() {
收率42;
}
const G1=makeGen();
const G2=makeGen();
G1 . next();//{ value: 42,done: false }
G1 . next();//{值:未定义,完成:真}
G1 . next();//没办法重置这个!
G2 . next();//{ value: 42,done: false }
.
const G3=makeGen();//创建新的生成器
G3 . next();//{value: 42,done: false}:
无限序列
迭代器表示序列,有点像数组。所以,我们应该能够将所有迭代器表示为数组,对吗?
然而,事实并非如此。数组在创建时需要立即分配,而迭代器是以延迟的方式使用的。迫切需要数组,因为创建一个有N个元素的数组需要首先创建/计算所有N个元素,以便将它们存储在数组中。相反,迭代器是懒惰的,因为序列中的下一个值只有在被使用时才会被创建/计算。
所以物理上不可能表示一个无限序列的数组(我们需要无限内存来存储无限项!),迭代器可以很容易地表示(而不是存储)序列。
让我们创建一个从1到正无穷数的无穷序列。与数组不同,这不需要无限内存,因为序列中的每个值只有在使用时才会被延迟计算。
函数* makeInfiniteSequence() {
var curr=1;
while (true) {
收益货币;
curr=1;
}
}
const is=makefinitesequence();
is . next();{值:1,完成:假}
is . next();{值:2,完成:假}
is . next();{值:3,完成:假}
.//永远不会结束有趣的事实:这类似于Python生成器表达式vs列表理解。虽然这两个表达式在功能上是相同的,但生成器表达式提供了内存优势,因为值的计算是延迟的,而列表理解是立即计算值并创建整个列表。
推荐:《javascript基础教程》
这就是用JavaScript分析生成器的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。