@@ -54,6 +54,20 @@ hw.next()
54
54
55
55
总结一下,调用Generator函数,返回一个遍历器对象,代表Generator函数的内部指针。以后,每次调用遍历器对象的` next ` 方法,就会返回一个有着` value ` 和` done ` 两个属性的对象。` value ` 属性表示当前的内部状态的值,是` yield ` 语句后面那个表达式的值;` done ` 属性是一个布尔值,表示是否遍历结束。
56
56
57
+ ES6没有规定,` function ` 关键字与函数名之间的星号,写在哪个位置。这导致下面的写法都能通过。
58
+
59
+ ``` javascript
60
+ function * foo (x , y ) { ··· }
61
+
62
+ function * foo (x , y ) { ··· }
63
+
64
+ function * foo (x , y ) { ··· }
65
+
66
+ function * foo (x , y ) { ··· }
67
+ ```
68
+
69
+ 由于Generator函数仍然是普通函数,所以一般的写法是上面的第三种,即星号紧跟在` function ` 关键字后面。本书也采用这种写法/。
70
+
57
71
### yield语句
58
72
59
73
由于Generator函数返回的遍历器对象,只有调用` next ` 方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。` yield ` 语句就是暂停标志。
@@ -219,36 +233,21 @@ function* foo(x) {
219
233
}
220
234
221
235
var a = foo (5 );
222
-
223
236
a .next () // Object{value:6, done:false}
224
237
a .next () // Object{value:NaN, done:false}
225
238
a .next () // Object{value:NaN, done:false}
239
+
240
+ var b = foo (5 );
241
+ b .next () // { value:6, done:false }
242
+ b .next (12 ) // { value:8, done:false }
243
+ b .next (13 ) // { value:42, done:true }
226
244
` ` `
227
245
228
246
上面代码中,第二次运行` next` 方法的时候不带参数,导致y的值等于` 2 * undefined ` (即` NaN ` ),除以3以后还是` NaN ` ,因此返回对象的` value` 属性也等于` NaN ` 。第三次运行` Next` 方法的时候不带参数,所以` z` 等于` undefined ` ,返回对象的` value` 属性等于` 5 + NaN + undefined ` ,即` NaN ` 。
229
247
230
- 如果向` next` 方法提供参数,返回结果就完全不一样了。
248
+ 如果向` next` 方法提供参数,返回结果就完全不一样了。上面代码第一次调用 ` b ` 的 ` next ` 方法时,返回 ` x + 1 ` 的值6;第二次调用 ` next ` 方法,将上一次 ` yield ` 语句的值设为12,因此 ` y ` 等于24,返回 ` y / 3 ` 的值8;第三次调用 ` next ` 方法,将上一次 ` yield ` 语句的值设为13,因此 ` z ` 等于13,这时 ` x ` 等于5, ` y ` 等于24,所以 ` return ` 语句的值等于42。
231
249
232
- ` ` ` javascript
233
- function * foo (x ) {
234
- var y = 2 * (yield (x + 1 ));
235
- var z = yield (y / 3 );
236
- return (x + y + z);
237
- }
238
-
239
- var it = foo (5 );
240
-
241
- it .next ()
242
- // { value:6, done:false }
243
- it .next (12 )
244
- // { value:8, done:false }
245
- it .next (13 )
246
- // { value:42, done:true }
247
- ` ` `
248
-
249
- 上面代码第一次调用` next` 方法时,返回` x+ 1 ` 的值6;第二次调用` next` 方法,将上一次` yield ` 语句的值设为12,因此` y` 等于24,返回` y / 3 ` 的值8;第三次调用` next` 方法,将上一次` yield ` 语句的值设为13,因此` z` 等于13,这时` x` 等于5,` y` 等于24,所以` return ` 语句的值等于42。
250
-
251
- 注意,由于` next` 方法的参数表示上一个` yield ` 语句的返回值,所以第一次使用` next` 方法时,不能带有参数。V8引擎直接忽略第一次使用` next` 方法时的参数,只有从第二次使用` next` 方法开始,参数才是有效的。
250
+ 注意,由于` next` 方法的参数表示上一个` yield ` 语句的返回值,所以第一次使用` next` 方法时,不能带有参数。V8引擎直接忽略第一次使用` next` 方法时的参数,只有从第二次使用` next` 方法开始,参数才是有效的。从语义上讲,第一个` next` 方法用来启动遍历器对象,所以不用带有参数。
252
251
253
252
如果想要第一次调用` next` 方法时,就能够输入值,可以在Generator函数外面再包一层。
254
253
@@ -272,6 +271,27 @@ wrapped().next('hello!')
272
271
273
272
上面代码中,Generator函数如果不用` wrapper` 先包一层,是无法第一次调用` next` 方法,就输入参数的。
274
273
274
+ 再看一个通过` next` 方法的参数,向Generator函数内部输入值的例子。
275
+
276
+ ` ` ` javascript
277
+ function * dataConsumer () {
278
+ console .log (' Started' );
279
+ console .log (` 1. ${ yield } ` );
280
+ console .log (` 2. ${ yield } ` );
281
+ return ' result' ;
282
+ }
283
+
284
+ let genObj = dataConsumer ();
285
+ genObj .next ();
286
+ // Started
287
+ genObj .next (' a' )
288
+ // 1. a
289
+ genObj .next (' b' )
290
+ // 2. b
291
+ ` ` `
292
+
293
+ 上面代码是一个很直观的例子,每次通过` next` 方法向Generator函数输入值,然后打印出来。
294
+
275
295
## for...of循环
276
296
277
297
` for ... of` 循环可以自动遍历Generator函数,且此时不再需要调用` next` 方法。
@@ -313,7 +333,7 @@ for (let n of fibonacci()) {
313
333
314
334
从上面代码可见,使用` for ... of` 语句时不需要使用next方法。
315
335
316
- 前面章节曾经介绍过,` for ... of` 循环、扩展运算符(...)、解构赋值和` Array .from ` 方法内部调用的,都是遍历器接口。这意味着,它们可以将Generator函数返回的Iterator对象,作为参数。
336
+ 前面章节曾经介绍过,` for ... of` 循环、扩展运算符(` ... ` )、解构赋值和` Array .from ` 方法内部调用的,都是遍历器接口。这意味着,它们可以将Generator函数返回的Iterator对象,作为参数。
317
337
318
338
` ` ` javascript
319
339
function * numbers () {
@@ -350,7 +370,30 @@ function* objectEntries(obj) {
350
370
}
351
371
352
372
let jane = { first: ' Jane' , last: ' Doe' };
353
- for (let [key,value] of objectEntries (jane)) {
373
+
374
+ for (let [key, value] of objectEntries (jane)) {
375
+ console .log (` ${ key} : ${ value} ` );
376
+ }
377
+ // first: Jane
378
+ // last: Doe
379
+ ` ` `
380
+
381
+ 上面代码中,对象` jane` 原生不具备Iterator接口,无法用` for ... of` 遍历。这时,我们通过Generator函数` objectEntries` 为它加上遍历器接口,就可以用` for ... of` 遍历了。加上遍历器接口的另一种写法是,将Generator函数加到对象的` Symbol .iterator ` 属性上面。
382
+
383
+ ` ` ` javascript
384
+ function * objectEntries () {
385
+ let propKeys = Object .keys (this );
386
+
387
+ for (let propKey of propKeys) {
388
+ yield [propKey, this [propKey]];
389
+ }
390
+ }
391
+
392
+ let jane = { first: ' Jane' , last: ' Doe' };
393
+
394
+ jane[Symbol .iterator ] = objectEntries;
395
+
396
+ for (let [key, value] of jane) {
354
397
console .log (` ${ key} : ${ value} ` );
355
398
}
356
399
// first: Jane
@@ -459,7 +502,7 @@ try {
459
502
// hello
460
503
` ` `
461
504
462
- 上面代码只输出hello就结束了,因为第二次调用next方法时 ,遍历器状态已经变成终止了。但是,如果使用throw命令抛出错误 ,不会影响遍历器状态。
505
+ 上面代码只输出 ` hello ` 就结束了,因为第二次调用 ` next ` 方法时 ,遍历器状态已经变成终止了。但是,如果使用 ` throw ` 命令抛出错误 ,不会影响遍历器状态。
463
506
464
507
` ` ` javascript
465
508
var gen = function * gen (){
@@ -765,7 +808,7 @@ for(let value of delegatingIterator) {
765
808
766
809
上面代码中,` delegatingIterator` 是代理者,` delegatedIterator` 是被代理者。由于` yield * delegatedIterator` 语句得到的值,是一个遍历器,所以要用星号表示。运行结果就是使用一个遍历器,遍历了多个Generator函数,有递归的效果。
767
810
768
- yield*语句等同于在Generator函数内部,部署一个for ...of循环 。
811
+ ` yield * ` 语句等同于在Generator函数内部,部署一个 ` for ... of ` 循环 。
769
812
770
813
` ` ` javascript
771
814
function * concat (iter1 , iter2 ) {
@@ -846,6 +889,26 @@ it.next()
846
889
847
890
上面代码在第四次调用` next` 方法的时候,屏幕上会有输出,这是因为函数` foo` 的` return ` 语句,向函数` bar` 提供了返回值。
848
891
892
+ 再看一个例子。
893
+
894
+ ` ` ` javascript
895
+ function * genFuncWithReturn () {
896
+ yield ' a' ;
897
+ yield ' b' ;
898
+ return ' The result' ;
899
+ }
900
+ function * logReturned (genObj ) {
901
+ let result = yield * genObj;
902
+ console .log (result);
903
+ }
904
+
905
+ [... logReturned (genFuncWithReturn ())]
906
+ // The result
907
+ // 值为 [ 'a', 'b' ]
908
+ ` ` `
909
+
910
+ 上面代码中,存在两次遍历。第一次是扩展运算符遍历函数` logReturned` 返回的遍历器对象,第二次是` yield * ` 语句遍历函数` genFuncWithReturn` 返回的遍历器对象。这两次遍历的效果是叠加的,最终表现为扩展运算符遍历函数` genFuncWithReturn` 返回的遍历器对象。所以,最后的数据表达式得到的值等于` [ ' a' , ' b' ]` 。但是,函数` genFuncWithReturn` 的` return ` 语句的返回值` The result` ,会返回给函数` logReturned` 内部的` result` 变量,因此会有终端输出。
911
+
849
912
` yield * ` 命令可以很方便地取出嵌套数组的所有成员。
850
913
851
914
` ` ` javascript
@@ -935,9 +998,36 @@ let obj = {
935
998
};
936
999
` ` `
937
1000
938
- ## 构造函数是Generator函数
1001
+ ## Generator函数的` this `
1002
+
1003
+ Generator函数总是返回一个遍历器,ES6规定这个遍历器是Generator函数的实例,也继承了Generator函数的` prototype` 对象上的方法。
1004
+
1005
+ ` ` ` javascript
1006
+ function * g () {}
1007
+
1008
+ g .prototype .hello = function () {
1009
+ return ' hi!' ;
1010
+ };
1011
+
1012
+ let obj = g ();
1013
+
1014
+ obj instanceof g // true
1015
+ obj .hello () // 'hi!'
1016
+ ` ` `
1017
+
1018
+ 上面代码表明,Generator函数` g` 返回的遍历器` obj` ,是` g` 的实例,而且继承了` g .prototype ` 。但是,如果把` g` 当作普通的构造函数,并不会生效,因为` g` 返回的总是遍历器对象,而不是` this ` 对象。
1019
+
1020
+ ` ` ` javascript
1021
+ function * g () {
1022
+ this .a = 11 ;
1023
+ }
1024
+
1025
+ let obj = g ();
1026
+ obj .a // undefined
1027
+ ` ` `
1028
+
1029
+ 上面代码中,Generator函数` g` 在` this ` 对象上面添加了一个属性` a` ,但是` obj` 对象拿不到这个属性。
939
1030
940
- 这一节讨论一种特殊情况:构造函数是Generator函数。
941
1031
942
1032
` ` ` javascript
943
1033
function * F (){
@@ -955,23 +1045,24 @@ function* F(){
955
1045
956
1046
上面代码中,由于` new F ()` 返回的是一个Iterator对象,具有next方法,所以上面的表达式为true。
957
1047
958
- 那么,这个时候怎么生成对象实例呢?
959
-
960
- 我们知道,如果构造函数调用时,没有使用new命令,那么内部的this对象,绑定当前构造函数所在的对象(比如window对象)。因此,可以生成一个空对象,使用bind方法绑定F内部的this。这样,构造函数调用以后,这个空对象就是F的实例对象了。
1048
+ 如果要把Generator函数当作正常的构造函数使用,可以采用下面的变通方法。首先,生成一个空对象,使用` bind` 方法绑定Generator函数内部的` this ` 。这样,构造函数调用以后,这个空对象就是Generator函数的实例对象了。
961
1049
962
1050
` ` ` javascript
1051
+ function * F (){
1052
+ yield this .x = 2 ;
1053
+ yield this .y = 3 ;
1054
+ }
963
1055
var obj = {};
964
1056
var f = F .bind (obj)();
965
1057
966
- f .next ();
967
- f .next ();
968
- f .next ();
1058
+ f .next (); // Object {value: 2, done: false}
1059
+ f .next (); // Object {value: 3, done: false}
1060
+ f .next (); // Object {value: undefined, done: true}
969
1061
970
- console .log (obj);
971
- // { x: 2, y: 3 }
1062
+ obj // { x: 2, y: 3 }
972
1063
` ` `
973
1064
974
- 上面代码中,首先是F内部的this对象绑定obj对象 ,然后调用它,返回一个Iterator对象。这个对象执行三次next方法(因为F内部有两个yield语句 ),完成F内部所有代码的运行。这时,所有内部属性都绑定在obj对象上了,因此obj对象也就成了F的实例 。
1065
+ 上面代码中,首先是 ` F ` 内部的 ` this ` 对象绑定 ` obj ` 对象 ,然后调用它,返回一个Iterator对象。这个对象执行三次 ` next ` 方法(因为 ` F ` 内部有两个 ` yield ` 语句 ),完成F内部所有代码的运行。这时,所有内部属性都绑定在 ` obj ` 对象上了,因此 ` obj ` 对象也就成了 ` F ` 的实例 。
975
1066
976
1067
## Generator函数推导
977
1068
0 commit comments