Javascript中迭代器和生成器的详细解释

处理集合中的每个项目是非常普通的操作。Javascript提供了许多方法来遍历一个集合,从简单的和每个图的(),()和理解(滤波器阵列阵列派生),Javascript 1.7,迭代器和生成器带来新的迭代机制在Javascript核心语法,并提供自定义…的循环行为的每个,机制。

迭代器

迭代器是每个访问设置序列的一个元素的对象和跟踪序列中的迭代的当前位置。在Javascript,一个迭代器是一个对象,它提供了一个下一个()方法,和下一个()方法返回序列中的下一个元素。该方法抛出StopIteration异常时序列中的所有元素的遍历。

一旦设置了迭代器对象,下一个()可以通过显式重复调用,也可以使用Javascript的for循环,每个循环中都称为隐式调用。

迭代器遍历一个简单对象和数组可以创建使用迭代器():

复制代码代码如下所示:

无功郎= {姓名:'javascript,出生年:1995 };

var =迭代器(Lang);



初始化完成后,可以调用下一个()方法依次访问对象的键值对:

复制代码代码如下所示:

var = it.next(双); / / 名字{关键

对=(它下一个);键到 生日

对= it.next(` StopIteration `); / /抛出一个异常



为…的环可以用来代替显式调用下一个()方法。环自动终止当StopIteration异常抛出。

复制代码代码如下所示:

var =迭代器(Lang);

对于(var对)

每次输出时,打印(对);{键,将}键改为



如果只想迭代对象的键值,可以将第二个参数传递到迭代器()函数中,其值为真:

复制代码代码如下所示:

var =迭代器(郎,true);

对于(var密钥)

打印(键);每个输出的键值



使用迭代器的一个优势()来访问对象的自定义属性添加到object.prototype不包括在序列对象。

Iterator()也可以用在数组上:

复制代码代码如下所示:

Var Langs = { 'javascript ','python ','haskell};

var =迭代器(Langs);

对于(var对)

打印(对);输出/索引的每一个迭代



像遍历对象一样,将true作为第二个参数传递的结果将是数组索引:

复制代码代码如下所示:

Var Langs = { 'javascript ','python ','haskell};

var =迭代器(Langs,真的);

因为(我在里面)

打印(i);输出0,然后是1,然后是2



使用让关键字、索引和值可以分配给分组变量的循环,和分配价值(解构的任务)可以解构。

复制代码代码如下所示:

Var Langs = { 'javascript ','python ','haskell};

var =迭代器(Langs);

为了(让{我,在它里面)

打印(+:+郎);输出0



声明自定义迭代器

某些有代表性的元素集的对象应以特定的方式迭代。

1。在范围上迭代的对象应该一个接一个地返回这个范围内包含的数字。

2。可以使用深度优先或广度优先访问树的叶节点。

三.迭代一个表示数据库查询结果的对象应该由行和行返回,即使整个结果集未被完全加载到单个数组中。

4。在无限数列的数学中(如斐波那契数列)的迭代器应在前提下不创建无限长的数据结构下的返回结果。

Javascript允许您编写自定义迭代逻辑的代码,并将其用于对象上。

我们创建一个包含两个低值和高值的简单范围对象:

复制代码代码如下所示:

函数范围(低,高){

this.low =低;

this.high =高;

}



现在我们创建一个自定义的迭代器返回的顺序包含在范围内的所有整数。迭代器接口要求我们提供一个()返回序列中的下一个元素或抛出StopIteration异常的方法。

复制代码代码如下所示:

功能rangeiterator(范围){

this.range =范围;

this.current = this.range.low;

}

rangeiterator.prototype.next =函数(){

如果(this.current >这个范围。高)

抛出StopIteration;

其他的

返回当前+ +;

};



我们的rangeiterator是由一系列实例实例化,同时保持当前的属性来跟踪当前序列的位置。

最后,为了rangeiterator能够结合的范围内,我们需要添加一个特殊的__iterator__范围的方法。当我们试图进行一个范围,它将被调用,返回一个实现迭代逻辑rangeiterator实例。

复制代码代码如下所示:

范围。原型。__iterator__ =函数(){

返回新的rangeiterator(本);

};



在完成自定义迭代器之后,我们可以迭代范围实例:

复制代码代码如下所示:

var范围=新范围(3, 5);

为(var I在范围内)

打印(i);输出3,然后是4,然后是5



生成器:构建迭代器的更好方法

虽然定制迭代器是一个有用的工具,但在创建时必须仔细规划,因为我们需要显式地维护它们的内部状态。

生成器提供了一个强大的功能:它允许您定义一个包含自己迭代算法的函数,它可以自动维护自己的状态。

生成器是一种特殊的函数,可以用作迭代器工厂。如果函数包含一个或多个表达式,则称为生成器。还需要在函数名之前添加(节点js)。

注意:只有代码块包含在(或更高版本)在HTML中可以使用yield关键字,XUL(XML用户界面语言)脚本标签不需要指定这个特殊的代码块或访问这些功能。

当生成器函数被调用的时候,身体不会立即实行,它返回一个生成器迭代器对象。每次下()发电机迭代器的方法被调用时,身体是执行到下一个表达式,并返回其结果。当函数结束或遇到return语句是一个StopIteration抛出异常。

通过一个例子给出了一个更好的例子。

复制代码代码如下所示:

功能simplegenerator(){

产量第一;

产量第二;

产量第三;

对于(var i = 0;i < 3;i + +)

屈服我;

}



var g = simplegenerator();

打印((g.next)); / /输出第一

打印((g.next)); / /输出二

打印((g.next)); / /输出第三

打印((g.next)); / /输出0

打印((g.next)); / /输出1

打印((g.next)); / /输出2

打印((g.next)); / /抛出StopIteration



生成器函数可直接作为一类__iterator__方法和代码量可以有效地降低其中一个自定义的迭代器是必要的。我们使用发电机来重写范围:

复制代码代码如下所示:

函数范围(低,高){

this.low =低;

this.high =高;

}

范围。原型。__iterator__ =函数(){

对于(var i = this.low;i < this.high;i++)

屈服我;

};

var范围=新范围(3, 5);

为(var I在范围内)

打印(i);输出3,然后是4,然后是5



并非所有的生成器都将终止,您可以创建一个表示无限序列的生成器:

复制代码代码如下所示:

函数斐波那契(){

VaR FN1 = 1;

VaR FN2 = 1;

当(1){

无功电流=FN2;

FN2=FN1;

FN1 = FN1 +电流;

产生的电流;

}

}



var序列=斐波那契();

打印((序列.下一步));1

打印((序列.下一步));1

打印((序列.下一步));2

打印((序列.下一步));3

打印((序列.下一步));5

打印((序列.下一步));8

打印((序列.下一步));13



生成器函数可以有参数,使用这些参数时,第一个电话称,发电机可以终止(使它抛出StopIteration异常)通过使用返回语句。以下的Fibonacci变异()有一个可选的极限参数,终止条件时触发功能。

复制代码代码如下所示:

函数斐波那契(限制){

VaR FN1 = 1;

VaR FN2 = 1;

当(1){

无功电流=FN2;

FN2=FN1;

FN1 = FN1 +电流;

如果(限流>限制)

返回;

产生的电流;

}

}



发电机的先进特性

生成器可以根据要求计算收益率返回值,这使得它能够代表先前昂贵的序列计算需求,甚至是上面所示的无限序列。

除了下一个()方法,发电机的迭代器对象也有送()方法,可以改变发电机的内部状态,通过发送值()将被视为最后的产量表现的结果,和发电机将暂停。在你使用发送到()通过指定的值的方法,你必须调用至少一次下()启动发电机。

下面的斐波那契生成器使用发送()方法重新启动序列:

复制代码代码如下所示:

函数斐波那契(){

VaR FN1 = 1;

VaR FN2 = 1;

当(1){

无功电流=FN2;

FN2=FN1;

FN1 = FN1 +电流;

var复位=屈服电流;

如果(复位){

FN1 = 1;

FN2 = 1;

}

}

}



var序列=斐波那契();

打印(sequence.next()); / / 1

打印(sequence.next()); / / 1

打印(sequence.next()); / / 2

打印(sequence.next()); / / 3

打印(sequence.next()); / / 5

打印(sequence.next()); / / 8

打印(sequence.next()); / / 13

打印(sequence.send(真正的)); / / 1

打印(sequence.next()); / / 1

打印(sequence.next()); / / 2

打印(sequence.next()); / / 3



注:有趣的是,打电话给(定义)和调用next()是完全相同的。然而,当发送()方法来启动一个新的发电机,未定义的其他值都将抛出TypeError异常。

可以调用抛出方法,并传递抛出的异常,强制生成器抛出异常。这个异常将从当前上下文中抛出并暂停生成器,类似于当前的收益执行,只替换抛出值语句。

如果没有产量在投掷过程中遇到的异常,异常将被传递到把()方法被调用,然后调用next()将引起StopIteration异常被抛出。

发电机有一个接近()的方法来迫使发电机停止。发电机的末端有以下作用:

1。将在所有生成器中执行有效的最后语句。

2。如果最后一句扔别人比了解任何例外,例外将被传递到接近对方()方法

3。生成器将终止

生成器表达式

数组派生的一个明显的缺点是它们导致整个数组在内存中构建。然而,当输入数组大或创建一个新的昂贵(或无限)数组生成器时,可能会出现问题。

生成器允许序列延迟计算(惰性计算),根据需要计算元素,生成器表达式与数组派生在语法上几乎相同。它使用括号而不是括号(用于…而不是每一个…但是,它创建一个生成器而不是一个数组,这样它就可以延迟计算。

假设我们有一个迭代器来迭代一个巨大的整数序列,我们需要创建一个新的迭代器来迭代偶数,数组派生将在内存中创建一个完整的偶数数组。

复制代码代码如下所示:

var(= i)2(});



生成器表达式将创建一个新迭代器,计算需要时按需的数量:

复制代码代码如下所示:

VaR IT2 =(我2,(我这));

打印(it2.next());第一个偶数 / /

打印(it2.next());在 / /第二偶数



当生成器用作函数的参数时,括号用作函数调用,这意味着可以省略最外括号:

复制代码代码如下所示:

VaR结果=行动(我2,(我这));





结束。