Skip to content

Commit 6129931

Browse files
committed
edit generator
1 parent b664561 commit 6129931

File tree

1 file changed

+141
-63
lines changed

1 file changed

+141
-63
lines changed

docs/generator.md

Lines changed: 141 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
# Generator 函数
22

3-
## 语法
4-
5-
### 简介
3+
## 简介
64

75
所谓Generator,有多种理解角度。首先,可以把它理解成一个函数的内部状态的遍历器,每调用一次,函数的内部状态发生一次改变(可以理解成发生某些事件)。ES6引入Generator函数,作用就是可以完全控制函数的内部状态的变化,依次遍历这些状态。
86

@@ -13,7 +11,7 @@
1311
function* helloWorldGenerator() {
1412
yield 'hello';
1513
yield 'world';
16-
return 'ending';
14+
return 'ending';
1715
}
1816

1917
var hw = helloWorldGenerator();
@@ -26,7 +24,7 @@ var hw = helloWorldGenerator();
2624

2725
```javascript
2826

29-
hw.next()
27+
hw.next()
3028
// { value: 'hello', done: false }
3129

3230
hw.next()
@@ -52,20 +50,22 @@ hw.next()
5250

5351
总结一下,Generator函数使用iterator接口,每次调用next方法的返回值,就是一个标准的iterator返回值:有着value和done两个属性的对象。其中,value是yield语句后面那个表达式的值,done是一个布尔值,表示是否遍历结束。
5452

55-
上一章说过,任意一个对象的Symbol.iterator属性,等于该对象的遍历器函数,即调用该函数会返回该对象的一个遍历器。由于Generator函数调用后返回自身的遍历器,所以Generator函数就是自身的遍历器函数,即它的Symbol.iterator属性指向自身
53+
上一章说过,任意一个对象的Symbol.iterator属性,等于该对象的遍历器函数,即调用该函数会返回该对象的一个遍历器。遍历器本身也是一个对象,它的Symbol.iterator属性执行后,返回自身
5654

5755
```javascript
5856

5957
function* gen(){
6058
// some code
6159
}
6260

63-
gen[Symbol.iterator]() === gen
61+
var g = gen();
62+
63+
g[Symbol.iterator]() === g
6464
// true
6565

6666
```
6767

68-
上面代码中,gen是一个Generator函数,它的Symbol.iterator属性就指向它自己
68+
上面代码中,gen是一个Generator函数,调用它会生成一个遍历器g。遍历器g的Symbol.iterator属性是一个遍历器函数,执行后返回它自己
6969

7070
由于Generator函数返回的遍历器,只有调用next方法才会遍历下一个成员,所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志,next方法遇到yield,就会暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回对象的value属性的值。当下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。如果没有再遇到新的yield语句,就一直运行到函数结束,将return语句后面的表达式的值,作为value属性的值,如果该函数没有return语句,则value属性的值为undefined。另一方面,由于yield后面的表达式,直到调用next方法时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
7171

@@ -82,7 +82,7 @@ function* f() {
8282
var generator = f();
8383

8484
setTimeout(function () {
85-
generator.next()
85+
generator.next()
8686
}, 2000);
8787

8888
```
@@ -111,7 +111,7 @@ var arr = [1, [[2, 3], 4], [5, 6]];
111111
var flat = function* (a){
112112
a.forEach(function(item){
113113
if (typeof item !== 'number'){
114-
yield* flat(item);
114+
yield* flat(item);
115115
} else {
116116
yield item;
117117
}
@@ -135,7 +135,7 @@ var flat = function* (a){
135135
for(var i =0;i<length;i++){
136136
var item = a[i];
137137
if (typeof item !== 'number'){
138-
yield* flat(item);
138+
yield* flat(item);
139139
} else {
140140
yield item;
141141
}
@@ -149,7 +149,7 @@ for (var f of flat(arr)){
149149

150150
```
151151
152-
### next方法的参数
152+
## next方法的参数
153153
154154
yield语句本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。
155155
@@ -199,7 +199,7 @@ it.next(13)
199199
200200
注意,由于next方法的参数表示上一个yield语句的返回值,所以第一次使用next方法时,不能带有参数。V8引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。
201201
202-
### for...of循环
202+
## for...of循环
203203
204204
for...of循环可以自动遍历Generator函数,且此时不再需要调用next方法。
205205
@@ -228,49 +228,49 @@ for (let v of foo()) {
228228
```javascript
229229

230230
function* fibonacci() {
231-
let [prev, curr] = [0, 1];
232-
for (;;) {
233-
[prev, curr] = [curr, prev + curr];
234-
yield curr;
235-
}
231+
let [prev, curr] = [0, 1];
232+
for (;;) {
233+
[prev, curr] = [curr, prev + curr];
234+
yield curr;
235+
}
236236
}
237237

238238
for (let n of fibonacci()) {
239-
if (n > 1000) break;
240-
console.log(n);
239+
if (n > 1000) break;
240+
console.log(n);
241241
}
242242

243243
```
244244
245245
从上面代码可见,使用for...of语句时不需要使用next方法。
246246
247-
### throw方法
247+
## throw方法
248248
249249
Generator函数还有一个特点,它可以在函数体外抛出错误,然后在函数体内捕获。
250250
251251
```javascript
252252

253253
var g = function* () {
254-
while (true) {
255-
try {
256-
yield;
257-
} catch (e) {
258-
if (e != 'a') {
259-
throw e;
260-
}
261-
console.log('内部捕获', e);
262-
}
254+
while (true) {
255+
try {
256+
yield;
257+
} catch (e) {
258+
if (e != 'a') {
259+
throw e;
260+
}
261+
console.log('内部捕获', e);
263262
}
263+
}
264264
};
265265

266266
var i = g();
267267
i.next();
268268

269269
try {
270-
i.throw('a');
271-
i.throw('b');
270+
i.throw('a');
271+
i.throw('b');
272272
} catch (e) {
273-
console.log('外部捕获', e);
273+
console.log('外部捕获', e);
274274
}
275275
// 内部捕获 a
276276
// 外部捕获 b
@@ -279,6 +279,101 @@ try {
279279
280280
上面代码中,遍历器i连续抛出两个错误。第一个错误被Generator函数体内的catch捕获,然后Generator函数执行完成,于是第二个错误被函数体外的catch捕获。
281281
282+
注意,上面代码的错误,是用遍历器的throw方法抛出的,而不是用throw命令抛出的。后者只能被函数体外的catch语句捕获。
283+
284+
```javascript
285+
var g = function* () {
286+
while (true) {
287+
try {
288+
yield;
289+
} catch (e) {
290+
if (e != 'a') {
291+
throw e;
292+
}
293+
console.log('内部捕获', e);
294+
}
295+
}
296+
};
297+
298+
var i = g();
299+
i.next();
300+
301+
try {
302+
throw new Error('a');
303+
throw new Error('b');
304+
} catch (e) {
305+
console.log('外部捕获', e);
306+
}
307+
// 外部捕获 [Error: a]
308+
```
309+
310+
上面代码之所以只捕获了a,是因为函数体外的catch语句块,捕获了抛出的a错误以后,就不会再继续执行try语句块了。
311+
312+
如果遍历器函数内部没有部署try...catch代码块,那么throw方法抛出的错误,将被外部try...catch代码块捕获。
313+
314+
```javascript
315+
var g = function* () {
316+
while (true) {
317+
yield;
318+
console.log('内部捕获', e);
319+
}
320+
};
321+
322+
var i = g();
323+
i.next();
324+
325+
try {
326+
i.throw('a');
327+
i.throw('b');
328+
} catch (e) {
329+
console.log('外部捕获', e);
330+
}
331+
// 外部捕获 a
332+
```
333+
334+
上面代码中,遍历器函数g内部,没有部署try...catch代码块,所以抛出的错误直接被外部catch代码块捕获。
335+
336+
如果遍历器函数内部部署了try...catch代码块,那么遍历器的throw方法抛出的错误,不影响下一次遍历,否则遍历直接终止。
337+
338+
```javascript
339+
var gen = function* gen(){
340+
yield console.log('hello');
341+
yield console.log('world');
342+
}
343+
344+
var g = gen();
345+
g.next();
346+
347+
try {
348+
g.throw();
349+
} catch (e) {
350+
g.next();
351+
}
352+
// hello
353+
```
354+
355+
上面代码只输出hello就结束了,因为第二次调用next方法时,遍历器状态已经变成终止了。但是,如果使用throw方法抛出错误,不会影响遍历器状态。
356+
357+
```javascript
358+
var gen = function* gen(){
359+
yield console.log('hello');
360+
yield console.log('world');
361+
}
362+
363+
var g = gen();
364+
g.next();
365+
366+
try {
367+
throw new Error();
368+
} catch (e) {
369+
g.next();
370+
}
371+
// hello
372+
// world
373+
```
374+
375+
上面代码中,throw命令抛出的错误不会影响到遍历器的状态,所以两次执行next方法,都取到了正确的操作。
376+
282377
这种函数体内捕获错误的机制,大大方便了对错误的处理。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数写一个错误处理语句。
283378
284379
```javascript
@@ -287,17 +382,17 @@ foo('a', function (a) {
287382
if (a.error) {
288383
throw new Error(a.error);
289384
}
290-
385+
291386
foo('b', function (b) {
292387
if (b.error) {
293388
throw new Error(b.error);
294389
}
295-
390+
296391
foo('c', function (c) {
297392
if (c.error) {
298393
throw new Error(c.error);
299394
}
300-
395+
301396
console.log(a, b, c);
302397
});
303398
});
@@ -324,42 +419,25 @@ function* g(){
324419

325420
```
326421
327-
如果Generator函数内部没有定义catch,那么throw方法抛出的错误,将被函数体的catch捕获。
328-
329-
```javascript
330-
331-
function *foo() { }
332-
333-
var it = foo();
334-
try {
335-
it.throw( "Oops!" );
336-
} catch (err) {
337-
console.log( "Error: " + err ); // Error: Oops!
338-
}
339-
340-
```
341-
342-
上面代码中,foo函数内部没有任何语句,throw抛出的错误被函数体外的catch捕获。
343-
344422
反过来,Generator函数内抛出的错误,也可以被函数体外的catch捕获。
345423
346424
```javascript
347425

348426
function *foo() {
349-
var x = yield 3;
350-
var y = x.toUpperCase();
351-
yield y;
427+
var x = yield 3;
428+
var y = x.toUpperCase();
429+
yield y;
352430
}
353431

354432
var it = foo();
355433

356434
it.next(); // { value:3, done:false }
357435

358436
try {
359-
it.next( 42 );
437+
it.next(42);
360438
}
361439
catch (err) {
362-
console.log( err );
440+
console.log(err);
363441
}
364442

365443
```
@@ -408,12 +486,12 @@ log(g());
408486
// fixing generator { value: 1, done: false }
409487
// fixing generator { value: 1, done: false }
410488
// caller done
411-
489+
412490
```
413491
414492
上面代码在Generator函数g抛出错误以后,再调用next方法,就不再执行下去了,一直停留在上一次的状态。
415493
416-
### yield*语句
494+
## yield*语句
417495
418496
如果yield命令后面跟的是一个遍历器,需要在yield命令后面加上星号,表明它返回的是一个遍历器。这被称为yield*语句。
419497
@@ -478,7 +556,7 @@ var it = bar();
478556
it.next(); //
479557
it.next(); //
480558
it.next(); //
481-
it.next(); // "v: foo"
559+
it.next(); // "v: foo"
482560
it.next(); //
483561

484562
```
@@ -546,15 +624,15 @@ let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
546624
// 遍历二叉树
547625
var result = [];
548626
for (let node of inorder(tree)) {
549-
result.push(node);
627+
result.push(node);
550628
}
551629

552630
result
553631
// ['a', 'b', 'c', 'd', 'e', 'f', 'g']
554632

555633
```
556634
557-
### 作为对象属性的Generator函数
635+
## 作为对象属性的Generator函数
558636
559637
如果一个对象的属性是Generator函数,可以简写成下面的形式。
560638

0 commit comments

Comments
 (0)