1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-27 20:32:21 +00:00

edit generator

This commit is contained in:
Ruan Yifeng 2015-04-03 07:26:07 +08:00
parent b664561c13
commit 6129931efc

View File

@ -1,8 +1,6 @@
# Generator 函数
## 语法
### 简介
## 简介
所谓Generator有多种理解角度。首先可以把它理解成一个函数的内部状态的遍历器每调用一次函数的内部状态发生一次改变可以理解成发生某些事件。ES6引入Generator函数作用就是可以完全控制函数的内部状态的变化依次遍历这些状态。
@ -13,7 +11,7 @@
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
return 'ending';
}
var hw = helloWorldGenerator();
@ -26,7 +24,7 @@ var hw = helloWorldGenerator();
```javascript
hw.next()
hw.next()
// { value: 'hello', done: false }
hw.next()
@ -52,7 +50,7 @@ hw.next()
总结一下Generator函数使用iterator接口每次调用next方法的返回值就是一个标准的iterator返回值有着value和done两个属性的对象。其中value是yield语句后面那个表达式的值done是一个布尔值表示是否遍历结束。
上一章说过任意一个对象的Symbol.iterator属性等于该对象的遍历器函数即调用该函数会返回该对象的一个遍历器。由于Generator函数调用后返回自身的遍历器所以Generator函数就是自身的遍历器函数即它的Symbol.iterator属性指向自身。
上一章说过任意一个对象的Symbol.iterator属性等于该对象的遍历器函数即调用该函数会返回该对象的一个遍历器。遍历器本身也是一个对象它的Symbol.iterator属性执行后返回自身。
```javascript
@ -60,12 +58,14 @@ function* gen(){
// some code
}
gen[Symbol.iterator]() === gen
var g = gen();
g[Symbol.iterator]() === g
// true
```
上面代码中gen是一个Generator函数它的Symbol.iterator属性就指向它自己。
上面代码中gen是一个Generator函数调用它会生成一个遍历器g。遍历器g的Symbol.iterator属性是一个遍历器函数执行后返回它自己。
由于Generator函数返回的遍历器只有调用next方法才会遍历下一个成员所以其实提供了一种可以暂停执行的函数。yield语句就是暂停标志next方法遇到yield就会暂停执行后面的操作并将紧跟在yield后面的那个表达式的值作为返回对象的value属性的值。当下一次调用next方法时再继续往下执行直到遇到下一个yield语句。如果没有再遇到新的yield语句就一直运行到函数结束将return语句后面的表达式的值作为value属性的值如果该函数没有return语句则value属性的值为undefined。另一方面由于yield后面的表达式直到调用next方法时才会执行因此等于为JavaScript提供了手动的“惰性求值”Lazy Evaluation的语法功能。
@ -82,7 +82,7 @@ function* f() {
var generator = f();
setTimeout(function () {
generator.next()
generator.next()
}, 2000);
```
@ -111,7 +111,7 @@ var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a){
a.forEach(function(item){
if (typeof item !== 'number'){
yield* flat(item);
yield* flat(item);
} else {
yield item;
}
@ -135,7 +135,7 @@ var flat = function* (a){
for(var i =0;i<length;i++){
var item = a[i];
if (typeof item !== 'number'){
yield* flat(item);
yield* flat(item);
} else {
yield item;
}
@ -149,7 +149,7 @@ for (var f of flat(arr)){
```
### next方法的参数
## next方法的参数
yield语句本身没有返回值或者说总是返回undefined。next方法可以带一个参数该参数就会被当作上一个yield语句的返回值。
@ -199,7 +199,7 @@ it.next(13)
注意由于next方法的参数表示上一个yield语句的返回值所以第一次使用next方法时不能带有参数。V8引擎直接忽略第一次使用next方法时的参数只有从第二次使用next方法开始参数才是有效的。
### for...of循环
## for...of循环
for...of循环可以自动遍历Generator函数且此时不再需要调用next方法。
@ -228,49 +228,49 @@ for (let v of foo()) {
```javascript
function* fibonacci() {
let [prev, curr] = [0, 1];
for (;;) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
let [prev, curr] = [0, 1];
for (;;) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (let n of fibonacci()) {
if (n > 1000) break;
console.log(n);
if (n > 1000) break;
console.log(n);
}
```
从上面代码可见使用for...of语句时不需要使用next方法。
### throw方法
## throw方法
Generator函数还有一个特点它可以在函数体外抛出错误然后在函数体内捕获。
```javascript
var g = function* () {
while (true) {
try {
yield;
} catch (e) {
if (e != 'a') {
throw e;
}
console.log('内部捕获', e);
}
while (true) {
try {
yield;
} catch (e) {
if (e != 'a') {
throw e;
}
console.log('内部捕获', e);
}
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b
@ -279,6 +279,101 @@ try {
上面代码中遍历器i连续抛出两个错误。第一个错误被Generator函数体内的catch捕获然后Generator函数执行完成于是第二个错误被函数体外的catch捕获。
注意上面代码的错误是用遍历器的throw方法抛出的而不是用throw命令抛出的。后者只能被函数体外的catch语句捕获。
```javascript
var g = function* () {
while (true) {
try {
yield;
} catch (e) {
if (e != 'a') {
throw e;
}
console.log('内部捕获', e);
}
}
};
var i = g();
i.next();
try {
throw new Error('a');
throw new Error('b');
} catch (e) {
console.log('外部捕获', e);
}
// 外部捕获 [Error: a]
```
上面代码之所以只捕获了a是因为函数体外的catch语句块捕获了抛出的a错误以后就不会再继续执行try语句块了。
如果遍历器函数内部没有部署try...catch代码块那么throw方法抛出的错误将被外部try...catch代码块捕获。
```javascript
var g = function* () {
while (true) {
yield;
console.log('内部捕获', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// 外部捕获 a
```
上面代码中遍历器函数g内部没有部署try...catch代码块所以抛出的错误直接被外部catch代码块捕获。
如果遍历器函数内部部署了try...catch代码块那么遍历器的throw方法抛出的错误不影响下一次遍历否则遍历直接终止。
```javascript
var gen = function* gen(){
yield console.log('hello');
yield console.log('world');
}
var g = gen();
g.next();
try {
g.throw();
} catch (e) {
g.next();
}
// hello
```
上面代码只输出hello就结束了因为第二次调用next方法时遍历器状态已经变成终止了。但是如果使用throw方法抛出错误不会影响遍历器状态。
```javascript
var gen = function* gen(){
yield console.log('hello');
yield console.log('world');
}
var g = gen();
g.next();
try {
throw new Error();
} catch (e) {
g.next();
}
// hello
// world
```
上面代码中throw命令抛出的错误不会影响到遍历器的状态所以两次执行next方法都取到了正确的操作。
这种函数体内捕获错误的机制,大大方便了对错误的处理。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数写一个错误处理语句。
```javascript
@ -287,17 +382,17 @@ foo('a', function (a) {
if (a.error) {
throw new Error(a.error);
}
foo('b', function (b) {
if (b.error) {
throw new Error(b.error);
}
foo('c', function (c) {
if (c.error) {
throw new Error(c.error);
}
console.log(a, b, c);
});
});
@ -324,31 +419,14 @@ function* g(){
```
如果Generator函数内部没有定义catch那么throw方法抛出的错误将被函数体的catch捕获。
```javascript
function *foo() { }
var it = foo();
try {
it.throw( "Oops!" );
} catch (err) {
console.log( "Error: " + err ); // Error: Oops!
}
```
上面代码中foo函数内部没有任何语句throw抛出的错误被函数体外的catch捕获。
反过来Generator函数内抛出的错误也可以被函数体外的catch捕获。
```javascript
function *foo() {
var x = yield 3;
var y = x.toUpperCase();
yield y;
var x = yield 3;
var y = x.toUpperCase();
yield y;
}
var it = foo();
@ -356,10 +434,10 @@ var it = foo();
it.next(); // { value:3, done:false }
try {
it.next( 42 );
it.next(42);
}
catch (err) {
console.log( err );
console.log(err);
}
```
@ -408,12 +486,12 @@ log(g());
// fixing generator { value: 1, done: false }
// fixing generator { value: 1, done: false }
// caller done
```
上面代码在Generator函数g抛出错误以后再调用next方法就不再执行下去了一直停留在上一次的状态。
### yield*语句
## yield*语句
如果yield命令后面跟的是一个遍历器需要在yield命令后面加上星号表明它返回的是一个遍历器。这被称为yield*语句。
@ -478,7 +556,7 @@ var it = bar();
it.next(); //
it.next(); //
it.next(); //
it.next(); // "v: foo"
it.next(); // "v: foo"
it.next(); //
```
@ -546,7 +624,7 @@ let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);
// 遍历二叉树
var result = [];
for (let node of inorder(tree)) {
result.push(node);
result.push(node);
}
result
@ -554,7 +632,7 @@ result
```
### 作为对象属性的Generator函数
## 作为对象属性的Generator函数
如果一个对象的属性是Generator函数可以简写成下面的形式。