迭代器和生成器

  • 版本网址缩略名: JavaScript/Guide/Iterators_and_Generators
  • 版本标题: 迭代器和生成器
  • 版本 id: 348625
  • 创建于:
  • 创建者: Joyce
  • 是否是当前版本?
  • 评论

修订内容

{{ Js_minversion_header("1.7") }}

处理一个集合中每一项是很常见的操作. JavaScript 提供了好几种方法来遍历一个集合, 从简单的 forfor each 循环至 map(), filter() and array comprehensions. 迭代器和生成器, 在 JavaScript 1.7中, 迭代器的概念属于核心语言中的重点 和提供了一种机制 来定义 for...in 的行为and for each 循环.

迭代器

迭代器是一个对象,知道如何存取物品从一个集合一次取出一项, 而跟踪它的当前序列所在的位置. 在 JavaScript中 迭代器是一个对象,它提供了一个next()方法返回序列中的下一个项目。当这个序列消亡时这个方法可以随时抛出一个StopIteration exception(异常)。

一旦创建,迭代器对象可以显式地通过不断调用next(),或隐式地使用JavaScript的 for...in and for each 构造.

简单的迭代器对象和数组可以使用Iterator()函数创建(下面是一个简单的对像):

var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);

一旦初始化,next()方法可以用来依次访问对象中的键-值:

var pair = it.next(); // Pair is ["name", "JavaScript"]
pair = it.next(); // Pair is ["birthYear", 1995]
pair = it.next(); // A StopIteration exception is thrown

一个 for...in 循环结构可以直接取代next()方法的调用。 当StopIteration exception异常抛出时这个循环会自动终止。

var it = Iterator(lang);
for (var pair in it)
  print(pair); // prints each [key, value] pair in turn

假如我们只想遍历一个对象的keys时,我们可以通过把第二个参数设置为true来实现:

var it = Iterator(lang, true);
for (var key in it)
  print(key); // prints each key in turn

使用 Iterator() 来访问对象内容的优点是对于那些已经加入对象的自定义属性(不用管属性名,一股脑的访问,或者之访问属性,上面就是这样)。它的原型不会包涵在序列中。

Iterator() 也可以用于数组:

var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs);
for (var pair in it)
  print(pair); // prints each [index, language] pair in turn

基于对象,通过传递true作为第二个参数,将会导致迭代结果发生数组索引上益(会吗?我也没搞明白,你知道的话请告诉我):

var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs, true);
for (var i in it)
  print(i); // prints 0, then 1, then 2

也可以指定块作用域变量(let是个javascript1.7的关键字,可以使用 let 建立只存在于 for 循环上下文中的变量)包括索引和值(index and value)在for…in循环中使用let关键字和非结构化赋值:

var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs);
for (let [i, lang] in it)
 print(i + ': ' + lang); // prints "0: JavaScript" etc.

定义自定义迭代器

某些对象遍历集合的项目时应该使用特定的方式遍历。

  • 遍历一个序列对象时应该是一个接着一个。
  • 遍历一棵树时应该使用深度优先或者广度优先方式。
  • 遍历一个数据库查询结果对象时应该是一条一条地遍历,即使整个结果并没有被加载到一个数组中。
  • 一个迭代器在一个无限的数学序列(如斐波那契序列)应该能够一个一个地返回结果而不创建一个无限长度的数据结构。

JavaScript可以让你编写代码来表示自定义迭代逻辑并链接到一个对象。

我们来创建一个简单的序列对象用来存放low和high。

function Range(low, high){
  this.low = low;
  this.high = high;
}

现在我们要创建一个遍历器,可以返回这个序列中的一序列包容性的整数,这个遍历器接口要求我们我们实现一个next()方法,这个next()方法返回这个序列或者抛出一个StopIteration exception异常。

function RangeIterator(range){
  this.range = range;
  this.current = this.range.low;
}
RangeIterator.prototype.next = function(){
  if (this.current > this.range.high)
    throw StopIteration;
  else
    return this.current++;
};

我们的RangeIterator被一个序列实例所序列化,并且维持它自己当前的属性属性所在位置。

最后,为了RangeIterator与Range序列对象,我们需要给Range写一个特殊的__iterator__方法,当我们试着遍历这个Range序列时会调用这个方法,并且应该返回一个RangeIterator遍历器实例。

Range.prototype.__iterator__ = function(){
  return new RangeIterator(this);
};

下面就写段代码来实验下我们自定义的遍历器吧:

var range = new Range(3, 5);
for (var i in range)
  print(i); // prints 3, then 4, then 5 in sequence

Generators: a better way to build Iterators

While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. Generators provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function which can maintain its own state.

A generator is a special type of function that works as a factory for iterators. A function becomes a generator if it contains one or more yield expressions.

{{ NoteStart() }}The yield keyword is only available to code blocks in HTML wrapped in a <script type="application/javascript;version=1.7"> block (or higher version). XUL script tags have access to these features without needing this special block.{{ NoteEnd() }}

When a generator function is called the body of the function does not execute straight away; instead, it returns a generator-iterator object. Each call to the generator-iterator's next() method will execute the body of the function up to the next yield expression and return its result. When either the end of the function or a return statement is reached, a StopIteration exception is thrown.

This is best illustrated with an example:

function simpleGenerator(){
  yield "first";
  yield "second";
  yield "third";
  for (var i = 0; i < 3; i++)
    yield i;
}

var g = simpleGenerator();
print(g.next()); // prints "first"
print(g.next()); // prints "second"
print(g.next()); // prints "third"
print(g.next()); // prints 0
print(g.next()); // prints 1
print(g.next()); // prints 2
print(g.next()); // StopIteration is thrown

A generator function can be used directly as the __iterator__ method of a class, greatly reducing the amount of code needed to create custom iterators. Here is our Range rewritten to use a generator:

function Range(low, high){
  this.low = low;
  this.high = high;
}
Range.prototype.__iterator__ = function(){
  for (var i = this.low; i <= this.high; i++)
    yield i;
};
var range = new Range(3, 5);
for (var i in range)
  print(i); // prints 3, then 4, then 5 in sequence

Not all generators terminate; it is possible to create a generator that represents an infinite sequence. The following generator implements the Fibonacci sequence, where each element is the sum of the two previous elements:

function fibonacci(){
  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    yield current;
  }
}

var sequence = fibonacci();
print(sequence.next()); // 1
print(sequence.next()); // 1
print(sequence.next()); // 2
print(sequence.next()); // 3
print(sequence.next()); // 5
print(sequence.next()); // 8
print(sequence.next()); // 13

Generator functions can take arguments, which are bound the first time the function is called. Generators can be terminated (causing them to raise a StopIteration exception) using a return statement. The following fibonacci() variant takes an optional limit argument, and will terminate once that limit has been passed.

function fibonacci(limit){
  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    if (limit && current > limit)
      return;
    yield current;
  }
}

Advanced generators

Generators compute their yielded values on demand, which allows them to efficiently represent sequences that are expensive to compute, or even infinite sequences as demonstrated above.

In addition to the next() method, generator-iterator objects also have a send() method which can be used to modify the internal state of the generator. A value passed to send() will be treated as the result of the last yield expression that paused the generator. You must start a generator by calling next() at least once before you can use send() to pass in a specific value.

Here is the fibonacci generator using send() to restart the sequence:

function fibonacci(){
  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    var reset = yield current;
    if (reset){
        fn1 = 1;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
print(sequence.next());     // 1
print(sequence.next());     // 1
print(sequence.next());     // 2
print(sequence.next());     // 3
print(sequence.next());     // 5
print(sequence.next());     // 8
print(sequence.next());     // 13
print(sequence.send(true)); // 1
print(sequence.next());     // 1
print(sequence.next());     // 2
print(sequence.next());     // 3
Note: As a point of interest, calling send(undefined) is equivalent to calling next(). However, starting a newborn generator with any value other than undefined when calling send() will result in a TypeError exception.

You can force a generator to throw an exception by calling its throw() method and passing the exception value it should throw. This exception will be thrown from the current suspended context of the generator, as if the yield that is currently suspended were instead a throw value statement.

If a yield is not encountered during the processing of the thrown exception, then the exception will propagate up through the call to throw(), and subsequent calls to next() will result in StopIteration being thrown.

Generators have a close() method that forces the generator to close itself. The effects of closing a generator are:

  1. Any finally clauses active in the generator function are run.
  2. If a finally clause throws any exception other than StopIteration, the exception is propagated to the caller of the close() method.
  3. The generator terminates.

Generator expressions

{{ Js_minversion_header("1.8") }}

A significant drawback of array comprehensions is that they cause an entire new array to be constructed in memory. When the input to the comprehension is itself a small array the overhead involved is insignificant — but when the input is a large array or an expensive (or indeed infinite) generator the creation of a new array can be problematic.

Generators enable lazy computation of sequences, with items calculated on-demand as they are needed. Generator expressions are syntactically almost identical to array comprehensions — they use parenthesis instead of braces (and for...in instead of for each...in) — but instead of building an array they create a generator that can execute lazily. You can think of them as short hand syntax for creating generators.

Suppose we have an iterator it which iterates over a large sequence of integers. We want to create a new iterator that will iterate over their doubles. An array comprehension would create a full array in memory containing the doubled values:

var doubles = [i * 2 for (i in it)];

A generator expression on the other hand would create a new iterator which would create doubled values on demand as they were needed:

var it2 = (i * 2 for (i in it));
print(it2.next()); // The first value from it, doubled
print(it2.next()); // The second value from it, doubled

When a generator expression is used as the argument to a function, the parenthesis used for the function call means that the outer parenthesis can be omitted:

var result = doSomething(i * 2 for (i in it));

{{ autoPreviousNext("JSGChapters") }}

修订版来源

<p>{{ Js_minversion_header("1.7") }}</p>
<p>处理一个集合中每一项是很常见的操作. JavaScript 提供了好几种方法来遍历一个集合, 从简单的 <code><a href="/zh-CN/JavaScript/Reference/Statements/for" title="zh-CN/Core_JavaScript_1.5_Reference/Statements/for">for</a></code> 和 <code><a href="/zh-CN/JavaScript/Reference/Statements/for_each...in" title="zh-CN/JavaScript/Reference/Statements/for each...in">for each</a></code> 循环至 <code><a href="/zh-CN/JavaScript/Reference/Global_Objects/Array/map" title="zh-CN/Core_JavaScript_1.5_Reference/Global_Objects/Array/map">map()</a></code>, <code><a href="/zh-CN/JavaScript/Reference/Global_Objects/Array/filter" title="zh-CN/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter">filter()</a></code> and <a href="/zh-CN/JavaScript/Guide/Predefined_Core_Objects#Array_comprehensions" title="zh-CN/JavaScript/Guide/Predefined Core Objects#Array comprehensions">array comprehensions</a>. 迭代器和生成器, 在 JavaScript 1.7中, 迭代器的概念属于核心语言中的重点 和提供了一种机制 来定义 <code><a href="/zh-CN/JavaScript/Reference/Statements/for...in" title="zh-CN/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code> 的行为and <code><a href="/zh-CN/JavaScript/Reference/Statements/for_each...in" title="zh-CN/JavaScript/Reference/Statements/for each...in">for each</a></code> 循环.</p>
<h2 id="Iterators">迭代器</h2>
<p>迭代器是一个对象,知道如何存取物品从一个集合一次取出一项, 而跟踪它的当前序列所在的位置. 在 JavaScript中 迭代器是一个对象,它提供了一个next()方法返回序列中的下一个项目。<code>当这个序列消亡时这个方法可以随时抛出一个</code><code>StopIteration</code> exception(异常)。</p>
<p>一旦创建,迭代器对象可以显式地通过不断调用next(),或隐式地使用JavaScript的 <code><a href="/zh-CN/JavaScript/Reference/Statements/for...in" title="zh-CN/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code> and <code><a href="/zh-CN/JavaScript/Reference/Statements/for_each...in" title="zh-CN/JavaScript/Reference/Statements/for each...in">for each</a></code> 构造.</p>
<p>简单的迭代器对象和数组可以使用Iterator()函数创建(下面是一个简单的对像):</p>
<pre class="brush: js">
var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);
</pre>
<p>一旦初始化,next()方法可以用来依次访问对象中的<span style="color:#0000cd;">键-值</span>:</p>
<pre class="brush: js">
var pair = it.next(); // Pair is ["name", "JavaScript"]
pair = it.next(); // Pair is ["birthYear", 1995]
pair = it.next(); // A StopIteration exception is thrown
</pre>
<p>一个 <code><a href="/zh-CN/JavaScript/Reference/Statements/for...in" title="zh-CN/Core_JavaScript_1.5_Reference/Statements/for...in">for...in</a></code> 循环结构可以直接取代next()方法的调用。 <code>当StopIteration</code> exception异常抛出时这个循环会自动终止。</p>
<pre class="brush: js">
var it = Iterator(lang);
for (var pair in it)
  print(pair); // prints each [key, value] pair in turn
</pre>
<p>假如我们只想遍历一个对象的keys时,我们可以通过把第二个参数设置为true来实现:</p>
<pre class="brush: js">
var it = Iterator(lang, true);
for (var key in it)
  print(key); // prints each key in turn
</pre>
<p>使用 <code>Iterator()</code> 来访问对象内容的优点是对于那些已经加入对象的自定义属性<code>(不用管属性名,一股脑的访问,或者之访问属性,上面就是这样)。它的原型不会包涵在序列中。</code></p>
<p><code>Iterator()</code> 也可以用于数组:</p>
<pre class="brush: js">
var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs);
for (var pair in it)
  print(pair); // prints each [index, language] pair in turn
</pre>
<p>基于对象,通过传递true作为第二个参数,将会导致迭代结果发生数组索引上益(会吗?我也没搞明白,你知道的话请告诉我):</p>
<pre class="brush: js">
var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs, true);
for (var i in it)
  print(i); // prints 0, then 1, then 2
</pre>
<p>也可以指定块作用域变量(<a href="https://developer.mozilla.org/zh-CN/docs/New_in_JavaScript_1.7" title="https://developer.mozilla.org/zh-CN/docs/New_in_JavaScript_1.7">let</a>是个javascript1.7的关键字,可以使用 <code>let</code> 建立只存在于 <code>for</code> 循环上下文中的变量)包括索引和值(index and value)在for…in循环中使用let关键字和非结构化赋值:</p>
<pre class="brush: js">
var langs = ['JavaScript', 'Python', 'C++'];
var it = Iterator(langs);
for (let [i, lang] in it)
 print(i + ': ' + lang); // prints "0: JavaScript" etc.
</pre>
<h2 id="Defining_custom_iterators">定义自定义迭代器</h2>
<p>某些对象遍历集合的项目时应该使用特定的方式遍历。</p>
<ul>
  <li>遍历一个序列对象时应该是一个接着一个。</li>
  <li>遍历一棵树时应该使用深度优先或者广度优先方式。</li>
  <li>遍历一个数据库查询结果对象时应该是一条一条地遍历,即使整个结果并没有被加载到一个数组中。</li>
  <li>一个迭代器在一个无限的数学序列(如斐波那契序列)应该能够一个一个地返回结果而不创建一个无限长度的数据结构。</li>
</ul>
<p>JavaScript可以让你编写代码来表示自定义迭代逻辑并链接到一个对象。</p>
<p>我们来创建一个简单的序列对象用来存放low和high。</p>
<pre class="brush: js">
function Range(low, high){
  this.low = low;
  this.high = high;
}
</pre>
<p>现在我们要创建一个遍历器,可以返回这个序列中的一序列包容性的整数,这个遍历器接口要求我们我们实现一个next()方法,这个next()方法返回这个序列或者抛出一个<code>StopIteration</code> exception异常。</p>
<pre class="brush: js">
function RangeIterator(range){
  this.range = range;
  this.current = this.range.low;
}
RangeIterator.prototype.next = function(){
  if (this.current &gt; this.range.high)
    throw StopIteration;
  else
    return this.current++;
};
</pre>
<p>我们的<code>RangeIterator被一个序列实例所序列化,并且维持它自己当前的属性属性所在位置。</code></p>
<p>最后,为了<code>RangeIterator与Range序列对象,我们需要给Range写一个特殊的</code><code>__iterator__方法,当我们试着遍历这个Range序列时会调用这个方法,并且应该返回一个</code><code>RangeIterator遍历器实例。</code></p>
<pre class="brush: js">
Range.prototype.__iterator__ = function(){
  return new RangeIterator(this);
};
</pre>
<p><code>下面就写段代码来实验下我们自定义的遍历器吧:</code></p>
<pre class="brush: js">
var range = new Range(3, 5);
for (var i in range)
  print(i); // prints 3, then 4, then 5 in sequence
</pre>
<h2 id="Generators.3A_a_better_way_to_build_Iterators">Generators: a better way to build Iterators</h2>
<p>While custom iterators are a useful tool, their creation requires careful programming due to the need to explicitly maintain their internal state. Generators provide a powerful alternative: they allow you to define an iterative algorithm by writing a single function which can maintain its own state.</p>
<p>A generator is a special type of function that works as a factory for iterators. A function becomes a generator if it contains one or more <code>yield</code> expressions.</p>
<p>{{ NoteStart() }}The <code>yield</code> keyword is only available to code blocks in HTML wrapped in a <code>&lt;script type="application/javascript;version=1.7"&gt;</code> block (or higher version). <a href="/en-US/docs/XUL" title="en-US/docs/XUL">XUL</a> script tags have access to these features without needing this special block.{{ NoteEnd() }}</p>
<p>When a generator function is called the body of the function does not execute straight away; instead, it returns a generator-iterator object. Each call to the generator-iterator's <code>next()</code> method will execute the body of the function up to the next <code>yield</code> expression and return its result. When either the end of the function or a <code>return</code> statement is reached, a <code>StopIteration</code> exception is thrown.</p>
<p>This is best illustrated with an example:</p>
<pre class="brush: js">
function simpleGenerator(){
  yield "first";
  yield "second";
  yield "third";
  for (var i = 0; i &lt; 3; i++)
    yield i;
}

var g = simpleGenerator();
print(g.next()); // prints "first"
print(g.next()); // prints "second"
print(g.next()); // prints "third"
print(g.next()); // prints 0
print(g.next()); // prints 1
print(g.next()); // prints 2
print(g.next()); // StopIteration is thrown
</pre>
<p>A generator function can be used directly as the <code>__iterator__</code> method of a class, greatly reducing the amount of code needed to create custom iterators. Here is our <code>Range</code> rewritten to use a generator:</p>
<pre class="brush: js">
function Range(low, high){
  this.low = low;
  this.high = high;
}
Range.prototype.__iterator__ = function(){
  for (var i = this.low; i &lt;= this.high; i++)
    yield i;
};
var range = new Range(3, 5);
for (var i in range)
  print(i); // prints 3, then 4, then 5 in sequence
</pre>
<p>Not all generators terminate; it is possible to create a generator that represents an infinite sequence. The following generator implements the Fibonacci sequence, where each element is the sum of the two previous elements:</p>
<pre class="brush: js">
function fibonacci(){
  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    yield current;
  }
}

var sequence = fibonacci();
print(sequence.next()); // 1
print(sequence.next()); // 1
print(sequence.next()); // 2
print(sequence.next()); // 3
print(sequence.next()); // 5
print(sequence.next()); // 8
print(sequence.next()); // 13
</pre>
<p>Generator functions can take arguments, which are bound the first time the function is called. Generators can be terminated (causing them to raise a <code>StopIteration</code> exception) using a <code>return</code> statement. The following <code>fibonacci()</code> variant takes an optional limit argument, and will terminate once that limit has been passed.</p>
<pre class="brush: js">
function fibonacci(limit){
  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    if (limit &amp;&amp; current &gt; limit)
      return;
    yield current;
  }
}
</pre>
<h2 id="Advanced_generators">Advanced generators</h2>
<p>Generators compute their yielded values on demand, which allows them to efficiently represent sequences that are expensive to compute, or even infinite sequences as demonstrated above.</p>
<p>In addition to the <code>next()</code> method, generator-iterator objects also have a <code>send()</code> method which can be used to modify the internal state of the generator. A value passed to <code>send()</code> will be treated as the result of the last <code>yield</code> expression that paused the generator. You must start a generator by calling <code>next()</code> at least once before you can use <code>send()</code> to pass in a specific value.</p>
<p>Here is the fibonacci generator using <code>send()</code> to restart the sequence:</p>
<pre class="brush: js">
function fibonacci(){
  var fn1 = 1;
  var fn2 = 1;
  while (1){
    var current = fn2;
    fn2 = fn1;
    fn1 = fn1 + current;
    var reset = yield current;
    if (reset){
        fn1 = 1;
        fn2 = 1;
    }
  }
}

var sequence = fibonacci();
print(sequence.next());     // 1
print(sequence.next());     // 1
print(sequence.next());     // 2
print(sequence.next());     // 3
print(sequence.next());     // 5
print(sequence.next());     // 8
print(sequence.next());     // 13
print(sequence.send(true)); // 1
print(sequence.next());     // 1
print(sequence.next());     // 2
print(sequence.next());     // 3</pre>
<div class="note">
  <strong>Note:</strong> As a point of interest, calling <code>send(undefined)</code> is equivalent to calling <code>next()</code>. However, starting a newborn generator with any value other than undefined when calling <code>send()</code> will result in a <code>TypeError</code> exception.</div>
<p>You can force a generator to throw an exception by calling its <code>throw()</code> method and passing the exception value it should throw. This exception will be thrown from the current suspended context of the generator, as if the <code>yield</code> that is currently suspended were instead a <code>throw <em>value</em></code> statement.</p>
<p>If a yield is not encountered during the processing of the thrown exception, then the exception will propagate up through the call to <code>throw()</code>, and subsequent calls to <code>next()</code> will result in <code>StopIteration</code> being thrown.</p>
<p>Generators have a <code>close()</code> method that forces the generator to close itself. The effects of closing a generator are:</p>
<ol>
  <li>Any <code>finally</code> clauses active in the generator function are run.</li>
  <li>If a <code>finally</code> clause throws any exception other than <code>StopIteration</code>, the exception is propagated to the caller of the <code>close()</code> method.</li>
  <li>The generator terminates.</li>
</ol>
<h2 id="Generator_expressions">Generator expressions</h2>
<p>{{ Js_minversion_header("1.8") }}</p>
<p>A significant drawback of <a href="/zh-CN/JavaScript/Guide/Predefined_Core_Objects#Array_comprehensions" title="zh-CN/JavaScript/Guide/Predefined Core Objects#Array comprehensions">array comprehensions</a> is that they cause an entire new array to be constructed in memory. When the input to the comprehension is itself a small array the overhead involved is insignificant — but when the input is a large array or an expensive (or indeed infinite) generator the creation of a new array can be problematic.</p>
<p>Generators enable lazy computation of sequences, with items calculated on-demand as they are needed. <em>Generator expressions</em> are syntactically almost identical to array comprehensions — they use parenthesis instead of braces (and <code>for...in</code> instead of <code>for each...in</code>) — but instead of building an array they create a generator that can execute lazily. You can think of them as short hand syntax for creating generators.</p>
<p>Suppose we have an iterator <code>it</code> which iterates over a large sequence of integers. We want to create a new iterator that will iterate over their doubles. An array comprehension would create a full array in memory containing the doubled values:</p>
<pre class="brush: js">
var doubles = [i * 2 for (i in it)];
</pre>
<p>A generator expression on the other hand would create a new iterator which would create doubled values on demand as they were needed:</p>
<pre class="brush: js">
var it2 = (i * 2 for (i in it));
print(it2.next()); // The first value from it, doubled
print(it2.next()); // The second value from it, doubled
</pre>
<p>When a generator expression is used as the argument to a function, the parenthesis used for the function call means that the outer parenthesis can be omitted:</p>
<pre class="brush: js">
var result = doSomething(i * 2 for (i in it));
</pre>
<p>{{ autoPreviousNext("JSGChapters") }}</p>
恢复到这个版本