1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
---
title: function*
slug: Web/JavaScript/Reference/Statements/function*
tags:
- ECMAScript 2015
- Generator
- JavaScript
- function*
- 函数
- 声明
- 迭代器
translation_of: Web/JavaScript/Reference/Statements/function*
---
<div>{{jsSidebar("Statements")}}</div>
<p><code><strong>function*</strong></code> 这种声明方式(<code>function</code>关键字后跟一个星号)会定义一个<strong><em>生成器函数</em> (</strong><em>generator function</em><strong>)</strong>,它返回一个 {{jsxref("Global_Objects/Generator","Generator")}} 对象。</p>
<div>{{EmbedInteractiveExample("pages/js/statement-functionasterisk.html")}}</div>
<div class="noinclude">
<p>你也可以使用构造函数 {{jsxref("GeneratorFunction")}} 或 {{jsxref("Operators/function*", "function* expression")}} 定义<strong><em>生成器函数</em> </strong>。</p>
</div>
<h2 id="语法">语法</h2>
<pre class="syntaxbox">function* <em>name</em>([<em>param</em>[, <em>param</em>[, ... <em>param</em>]]]) { <em>statements</em> }
</pre>
<dl>
<dt><code>name</code></dt>
<dd>函数名</dd>
</dl>
<dl>
<dt><code>param</code></dt>
<dd>要传递给函数的一个参数的名称,一个函数最多可以有255个参数。</dd>
</dl>
<dl>
<dt><code>statements</code></dt>
<dd>普通JS语句。</dd>
</dl>
<h2 id="描述">描述</h2>
<p><strong>生成器函数</strong>在执行时能暂停,后面又能从暂停处继续执行。</p>
<p>调用一个<strong>生成器函数</strong>并不会马上执行它里面的语句,而是返回一个这个生成器的 <strong>迭代器</strong> <strong>( <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#iterator">iterator</a> )对象</strong>。当这个迭代器的 <code>next() </code>方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现{{jsxref("Operators/yield", "yield")}}的位置为止,{{jsxref("Operators/yield", "yield")}} 后紧跟迭代器要返回的值。或者如果用的是 {{jsxref("Operators/yield*", "yield*")}}(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。</p>
<p><code>next()</code>方法返回一个对象,这个对象包含两个属性:value 和 done,value 属性表示本次 <code>yield </code>表达式的返回值,done 属性为布尔类型,表示生成器后续是否还有<code> yield </code>语句,即生成器函数是否已经执行完毕并返回。</p>
<p>调用 <code>next()</code>方法时,如果传入了参数,那么这个参数会传给<strong>上一条执行的 yield语句左边的变量</strong>,例如下面例子中的<code> x </code>:</p>
<pre class="brush: js">function *gen(){
yield 10;
x=yield 'foo';
yield x;
}
var gen_obj=gen();
console.log(gen_obj.next());// 执行 yield 10,返回 10
console.log(gen_obj.next());// 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(100));// 将 100 赋给上一条 yield 'foo' 的左值,即执行 x=100,返回 100
console.log(gen_obj.next());// 执行完毕,value 为 undefined,done 为 true</pre>
<p>当在生成器函数中显式 <code>return </code>时,会导致生成器立即变为完成状态,即调用 <code>next()</code> 方法返回的对象的 <code>done </code>为 <code>true</code>。如果 <code>return </code>后面跟了一个值,那么这个值会作为<strong>当前</strong>调用 <code>next()</code> 方法返回的 value 值。</p>
<h2 id="示例">示例</h2>
<h3 id="简单示例">简单示例</h3>
<pre class="brush: js">function* idMaker(){
var index = 0;
while(index<3)
yield index++;
}
var gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // undefined
</pre>
<h3 id="生成器也可以接收参数:">生成器也可以接收参数:</h3>
<pre class="brush: js">function* idMaker(){
var index = arguments[0] || 0;
while(true)
yield index++;
}
var gen = idMaker(5);
console.log(gen.next().value); // 5
console.log(gen.next().value); // 6</pre>
<h3 id="yield*的示例">yield*的示例</h3>
<pre class="brush: js">function* anotherGenerator(<strong>i</strong>) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(<strong>i</strong>){
yield i;
yield* anotherGenerator(<strong>i</strong>);// 移交执行权
yield i + 10;
}
var gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
</pre>
<h3 id="传递参数">传递参数</h3>
<pre class="brush: js">function *createIterator() {
let first = yield 1;
let second = yield first + 2; // <strong>4</strong> + 2
// first =4 是next<strong>(4)将参数赋给上一条的</strong>
yield second + 3; // <strong>5</strong> + 3
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next<strong>(4)</strong>); // "{ value: 6, done: false }"
console.log(iterator.next(<strong>5</strong>)); // "{ value: 8, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"</pre>
<h3 id="显式返回">显式返回</h3>
<pre class="brush: js">function* yieldAndReturn() {
yield "Y";
return "R";//显式返回处,可以观察到 done 也立即变为了 true
yield "unreachable";// 不会被执行了
}
var gen = yieldAndReturn()
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }</pre>
<h3 id="生成器函数不能当构造器使用">生成器函数不能当构造器使用</h3>
<pre class="brush: js">function* f() {}
var obj = new f; // throws "TypeError: f is not a constructor"</pre>
<h3 id="使用迭代器遍历二维数组并转换成一维数组:">使用迭代器遍历二维数组并转换成一维数组:</h3>
<blockquote>
<pre class="brush: js">function* iterArr(arr) { //迭代器返回一个迭代器对象
if (Array.isArray(arr)) { // 内节点
for(let i=0; i < arr.length; i++) {
yield* iterArr(arr[i]); // (*)递归
}
} else { // 离开
yield arr;
}
}
// 使用 for-of 遍历:
var arr = ['a', ['b', 'c'], ['d', 'e']];
for(var x of iterArr(arr)) {
console.log(x); // a b c d e
}
// 或者直接将迭代器展开:
var arr = [ 'a', ['b',[ 'c', ['d', 'e']]]];
var gen = iterArr(arr);
arr = [...gen]; // ["a", "b", "c", "d", "e"]</pre>
</blockquote>
<h2 id="规范">规范</h2>
<table class="standard-table">
<thead>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{SpecName('ES2015', '#', 'function*')}}</td>
<td>{{Spec2('ES2015')}}</td>
<td>Initial definition.</td>
</tr>
<tr>
<td>{{SpecName('ESDraft', '#', 'function*')}}</td>
<td>{{Spec2('ESDraft')}}</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="浏览器兼容性">浏览器兼容性</h2>
<div>
<p>{{Compat("javascript.statements.generator_function")}} </p>
</div>
<h2 id="Firefox浏览器具体事项">Firefox浏览器具体事项</h2>
<h4 id="Firefox_26之前的生成器和迭代器">Firefox 26之前的生成器和迭代器</h4>
<p>旧版本的Firefox实施了旧版本的生成器提案。旧版中用普通的<code>function关键字定义</code>(没有星号).</p>
<h4 id="IteratorResult不再抛出错误"><code>IteratorResult</code>不再抛出错误</h4>
<p>从Gecko 29 {{geckoRelease(29)}}开始,完成的生成器函数不再抛出{{jsxref("TypeError")}} "generator has already finished". 而是返回一个<code>IteratorResult</code>对象:{ value: undefined, done: true } ({{bug(958951)}})。</p>
<h2 id="相关链接">相关链接</h2>
<ul>
<li>{{jsxref("Operators/function*", "function* expression")}}</li>
<li>{{jsxref("GeneratorFunction")}} object</li>
<li><a href="/zh-CN/docs/Web/JavaScript/Guide/The_Iterator_protocol">迭代器协议</a></li>
<li>{{jsxref("Operators/yield", "yield")}}</li>
<li>{{jsxref("Operators/yield*", "yield*")}}</li>
<li>{{jsxref("Function")}} object</li>
<li>{{jsxref("Statements/function", "function declaration")}}</li>
<li>{{jsxref("Operators/function", "function expression")}}</li>
<li>{{jsxref("Functions_and_function_scope", "Functions and function scope")}}</li>
<li>其他网络资源:
<ul>
<li><a href="http://facebook.github.io/regenerator/">Regenerator</a> an ES2015 generator compiler to ES5</li>
<li><a href="http://www.youtube.com/watch?v=qbKWsbJ76-s">Forbes Lindesay: Promises and Generators: control flow utopia -- JSConf EU 2013</a></li>
<li><a href="https://www.youtube.com/watch?v=ZrgEZykBHVo&list=PLuoyIZT5fPlG44bPq50Wgh0INxykdrYX7&index=1">Hemanth.HM: The New gen of *gen(){}</a></li>
<li><a href="http://taskjs.org/">Task.js</a></li>
</ul>
</li>
</ul>
|