参考个人博客:http://www.zhuzhuman.com/nav-1/type-1/article/23.html
generator(生成器)是ES6标准引入的新的数据类型。最大特点就是可以交出函数的执行权(即暂停执行)。一个generator看上去像一个函数,区别就是函数名前面多了一个星号 *,但可以返回多次。与yield命令配合,可以实现暂停执行的功能
1. 基本使用方法
function* testGenerator(a) {
console.log('a');
yield ++a;
console.log('b');
yield ++a;
console.log('c');
return ++a;
console.log('d');
}
var gener = testGenerator(5); // 函数里的log并没有输出,所以函数并没有执行,
var gener2 = testGenerator(1); // gener 和 gener2 之间互不印象,
console.log(gener); // testGenerator{<suspended>} (generator对象), 说明返回的是一个对象
var yield1 = gener.next(); // a (输出了一个a,第一个yield前的log执行了)
console.log(yield1); // {value: 6, done: false} next函数返回的是一个 包含值和状态的 对象
console.log(yield1.value); // 6 (value是yield的返回的值)
console.log(yield1.done); // false (done是yield的返回状态,false代表目前处于暂停状态,还没执行完)
var yield2 = gener.next(); // b (输出一个b,可见函数是接着第一个yield后面的代码继续执行的,到第二个yield停止)
console.log(yield2); // {value: 7, done: false}
console.log(yield2.value); // 7 (第二次返回了7,说明此时的变量值是接着 第一个yield的 变量值(此时a=6)使用的)
console.log(yield2.done); // false ()
var yield3 = gener.next(); // c (输出一个c,可见函数是接着第二个yield后面的代码继续执行的,到return停止)
console.log(yield3); // {value: 8, done: true}
console.log(yield3.value); // 8
console.log(yield3.done); // true
var yield4 = gener.next(); // (没有输出,说明执行的renturn之后不会再往下执行)
console.log(yield4); // {value: undefined, done: true} (执行完return,以后再 next() 返回的都是 undefind)
2. next方法
- 遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性的值。
- 再次调用next方法时,再继续往下执行,直到遇到下一个yield语句。
- 如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
- 如果该函数没有return语句,则返回的对象的value属性值为undefined。
- next方法可以有参数, next方法参数的作用,是覆盖掉上一个yield语句的值。
function* testNext() {
var a = 1;
var b = yield ++a;
console.log(b);
var c = yield ++a;
console.log(c);
return ++a;
}
var gener3 = testNext();
// var v1 = gener3.next(3); // 第一次执行next的时候传值,由于此时并没有 上一个 yield 的值,所以此时传值不起作用
var v1 = gener3.next(); // (没有输出 语句:var b = yield ++a; 先执行等号右边的运算,此时执行到 yield ++a, 返回++a(2) 逻辑暂停,即此时 b并没有附上值。)
console.log(v1); // {value: 2, done: false}
var v2 = gener3.next(6); // 6 执行第二个next,此时,有传参,先将上一个 yield 的值(++a 2) 替换为 参数6,然后接着执行语句,给b赋值, 所以此时 b = 6
console.log(v2); // {value: 3, done: false} 第一个yield执行完,a的值为2, 所以第二个 yield 返回 ++a 时候,返回值为3
var v3 = gener3.next(10); // 10
console.log(v3); // {value: 4, done: true}, 同上
3. throw方法
- 在函数体外抛出错误,然后在Generator函数体内捕获。
function* testThrow() {
console.log(1)
yield 1;
try {
console.log(2)
yield 2;
console.log(3)
yield 3;
} catch (e) {
console.log('第一个catch:' + e);
}
console.log(4);
yield 4;
try{
console.log(5);
yield 5;
} catch (e) {
console.log('第二个catch:' + e);
}
console.log(6);
return 6;
}
var gener4 = testThrow();
var t1 = gener4.next(); // 1
console.log(t1); // {value: 1, done: false}
// gener4.throw('试着抛出个异常 ^_^!'); // 报错:Uncaught 试着抛出个异常 ^_^!; (由于第一次next并没有try...catch... 这时手动抛出异常,异常无法被捕获,代码不再往下执行了。之后再调用 next 将返回 {value: undefined, done: true})
var t2 = gener4.next(); // 2
console.log(t2); // {value: 2, done: false}
gener4.throw('这个异常1可以被catch到 ^_^'); // 第一个catch:这个异常1可以被catch到 ^_^ (要抛出的异常,被generator里的catch给捕获到了)
// 4 // 当捕获到异常后,代码会自动再执行一次 next() 方法,此时控制台输出了四,说明try 里面的第二个 yield 并没有被执行,可见,一个try代码块里面只能有一个yield生效
// gener4.throw('这个异常2不可以被catch到 ^_^'); // Uncaught 这个异常2不可以被catch到 ^_^ (再次手动抛出异常就不能被捕获了,此时代码已经执行到 yield 4; 了)
var t3 = gener4.next(); // 5
console.log(t3); // {value: 5, done: false}
gener4.throw('这个异常3可以被catch到 ^_^'); // 第二个catch:这个异常3可以被catch到 ^_^
// 6 // catch捕获后,执行了下一个next(); 此时执行完,已经执行完 return 6; 了
var t4 = gener4.next();
console.log(t4); // {value: undefined, done: true} (上一个catch之后,已经把return执行了,所以再次执行 next 返回 {value: undefined, done: true})
4. return 方法
- 可以返回给定的值(覆盖本次yield的值),并且终结遍历Generator函数。
function* testReturn() {
yield 'a';
yield 'b';
yield 'c';
}
var gener5 = testReturn();
var g1 = gener5.next();
console.log(g1); // {value: "a", done: false}
var g2 = gener5.return('e');
console.log(g2); // {value: "e", done: true} (强行第二次yield的值b为 指定值的 e,并终止generator函数执行,)
var g3 = gener5.next();
console.log(g3); // {value: undefined, done: true} (上面已终止执行)
5. for…of…
- for…of…循环可以自动遍历Generator函数生成的Iterator对象,且此时不再需要调用next方法
function* testForOf() {
yield 'J';
yield 'Q';
yield 'K';
return 'A';
}
var gener6 = testForOf();
for(var f of gener6) {
console.log(f);
// J (for...of循环可以自动遍历Generator函数时生成的Iterator对象,且此时不再需要调用next方法。)
// Q
// K (一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象, 所以没输出 A)
}
var f1 = gener6.next();
console.log(f1); // {value: undefined, done: true} (for...of...相当于调用了next,当for循环结束,generator已经执行到 done:true 状态了)
6. yield 语句
- yield语句只能用于function的作用域,如果function的内部还定义了其他的普通函数,则函数内部不允许使用yield语句。
- yield* 在Generater函数内部,调用另一个Generator函数
function* testYield() {
yield 'x';
// function inFn() {
// yield 'y'; // Uncaught SyntaxError: Unexpected string (报错, 普通函数里无法使用yield)
// }
yield 'y';
}
function* testYield2() {
yield 'xx';
testYield();
yield 'yy';
}
for(var m of testYield2()) {
console.log(m);
// xx
// yy
// (在testYield2中无法访问到testYield中的值)
}
function* testYield3() {
yield 'xxx';
yield* testYield();
yield 'yyy';
}
for(var n of testYield3()) {
console.log(n);
// xxx
// x
// y
// yyy
// (yield* 在testYield3中可以访问到testYield中的值)
}