77在形式上,Generator是一个普通函数,但是有两个特征。一是,function命令与函数名之间有一个星号;二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态(yield语句在英语里的意思就是“产出”)。
88
99``` javascript
10-
1110function * helloWorldGenerator () {
1211 yield ' hello' ;
1312 yield ' world' ;
1413 return ' ending' ;
1514}
1615
1716var hw = helloWorldGenerator ();
18-
1917```
2018
2119上面代码定义了一个Generator函数helloWorldGenerator,它的遍历器有两个成员“hello”和“world”。调用这个函数,就会得到遍历器。
@@ -127,7 +125,6 @@ for (var f of flat(arr)){
127125上面代码也会产生句法错误,因为forEach方法的参数是一个普通函数,但是在里面使用了yield语句。一种修改方法是改用for循环。
128126
129127` ` ` javascript
130-
131128var arr = [1 , [[2 , 3 ], 4 ], [5 , 6 ]];
132129
133130var flat = function * (a ){
@@ -146,15 +143,13 @@ for (var f of flat(arr)){
146143 console .log (f);
147144}
148145// 1, 2, 3, 4, 5, 6
149-
150146` ` `
151147
152148## next方法的参数
153149
154150yield语句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。
155151
156152` ` ` javascript
157-
158153function * f () {
159154 for (var i= 0 ; true ; i++ ) {
160155 var reset = yield i;
@@ -167,7 +162,6 @@ var g = f();
167162g .next () // { value: 0, done: false }
168163g .next () // { value: 1, done: false }
169164g .next (true ) // { value: 0, done: false }
170-
171165` ` `
172166
173167上面代码先定义了一个可以无限运行的Generator函数f,如果next方法没有参数,每次运行到yield语句,变量reset的值总是undefined。当next方法带一个参数true时,当前的变量reset就被重置为这个参数(即true),因此i会等于-1,下一轮循环就会从-1开始递增。
@@ -177,7 +171,24 @@ g.next(true) // { value: 0, done: false }
177171再看一个例子。
178172
179173` ` ` javascript
174+ function * foo (x ) {
175+ var y = 2 * (yield (x + 1 ));
176+ var z = yield (y / 3 );
177+ return (x + y + z);
178+ }
179+
180+ var a = foo (5 );
180181
182+ a .next () // Object{value:6, done:false}
183+ a .next () // Object{value:NaN, done:false}
184+ a .next () // Object{value:NaN, done:false}
185+ ` ` `
186+
187+ 上面代码中,第二次运行next方法的时候不带参数,导致y的值等于` 2 * undefined ` (即NaN),除以3以后还是NaN,因此返回对象的value属性也等于NaN。第三次运行Next方法的时候不带参数,所以z等于undefined,返回对象的value属性等于` 5 + NaN + undefined ` ,即NaN。
188+
189+ 如果向next方法提供参数,返回结果就完全不一样了。
190+
191+ ` ` ` javascript
181192function * foo (x ) {
182193 var y = 2 * (yield (x + 1 ));
183194 var z = yield (y / 3 );
@@ -192,7 +203,6 @@ it.next(12)
192203// { value:8, done:false }
193204it .next (13 )
194205// { value:42, done:true }
195-
196206` ` `
197207
198208上面代码第一次调用next方法时,返回` x+ 1 ` 的值6;第二次调用next方法,将上一次yield语句的值设为12,因此y等于24,返回` y / 3 ` 的值8;第三次调用next方法,将上一次yield语句的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。
@@ -249,15 +259,12 @@ for (let n of fibonacci()) {
249259Generator函数还有一个特点,它可以在函数体外抛出错误,然后在函数体内捕获。
250260
251261` ` ` javascript
252-
253262var g = function * () {
254263 while (true ) {
255264 try {
256265 yield ;
257266 } catch (e) {
258- if (e != ' a' ) {
259- throw e;
260- }
267+ if (e != ' a' ) throw e;
261268 console .log (' 内部捕获' , e);
262269 }
263270 }
@@ -274,7 +281,6 @@ try {
274281}
275282// 内部捕获 a
276283// 外部捕获 b
277-
278284` ` `
279285
280286上面代码中,遍历器i连续抛出两个错误。第一个错误被Generator函数体内的catch捕获,然后Generator函数执行完成,于是第二个错误被函数体外的catch捕获。
@@ -287,9 +293,7 @@ var g = function* () {
287293 try {
288294 yield ;
289295 } catch (e) {
290- if (e != ' a' ) {
291- throw e;
292- }
296+ if (e != ' a' ) throw e;
293297 console .log (' 内部捕获' , e);
294298 }
295299 }
@@ -436,52 +440,51 @@ try {
436440
437441上面代码中,第二个next方法向函数体内传入一个参数42,数值是没有toUpperCase方法的,所以会抛出一个TypeError错误,被函数体外的catch捕获。
438442
439- 一旦Generator执行过程中抛出错误,就不会再执行下去了。如果此后还调用next方法,将一直返回发生错误前的那个值 。
443+ 一旦Generator执行过程中抛出错误,就不会再执行下去了。如果此后还调用next方法,将返回一个value属性等于undefined、done属性等于true的对象,即JavaScript引擎认为这个Generator已经运行结束了 。
440444
441445` ` ` javascript
442-
443446function * g () {
444447 yield 1 ;
445448 console .log (' throwing an exception' );
446449 throw new Error (' generator broke!' );
447450 yield 2 ;
451+ yield 3 ;
448452}
449453
450454function log (generator ) {
451455 var v;
452456 console .log (' starting generator' );
453457 try {
454458 v = generator .next ();
455- console .log (' got back ' , v);
459+ console .log (' 第一次运行next方法 ' , v);
456460 } catch (err) {
457- console .log (' fixing generator ' , v);
461+ console .log (' 捕捉错误 ' , v);
458462 }
459463 try {
460464 v = generator .next ();
461- console .log (' got back ' , v);
465+ console .log (' 第二次运行next方法 ' , v);
462466 } catch (err) {
463- console .log (' fixing generator ' , v);
467+ console .log (' 捕捉错误 ' , v);
464468 }
465469 try {
466470 v = generator .next ();
467- console .log (' got back ' , v);
471+ console .log (' 第三次运行next方法 ' , v);
468472 } catch (err) {
469- console .log (' fixing generator ' , v);
473+ console .log (' 捕捉错误 ' , v);
470474 }
471475 console .log (' caller done' );
472476}
473477
474478log (g ());
475479// starting generator
476- // got back { value: 1, done: false }
480+ // 第一次运行next方法 { value: 1, done: false }
477481// throwing an exception
478- // fixing generator { value: 1, done: false }
479- // fixing generator { value: 1 , done: false }
482+ // 捕捉错误 { value: 1, done: false }
483+ // 第三次运行next方法 { value: undefined , done: true }
480484// caller done
481-
482485` ` `
483486
484- 上面代码在Generator函数g抛出错误以后,再调用next方法,就不再执行下去了,一直停留在上一次的状态 。
487+ 上面代码一共三次运行next方法,第二次运行的时候会抛出错误,然后第三次运行的时候,Generator函数就已经结束了,不再执行下去了 。
485488
486489## yield*语句
487490
0 commit comments