生成器是ES6新增的功能,标志是加一个*.
1.生成器函数的执行过程
生成器函数和普通函数不一样,普通函数是一旦调用一定会执行完,生成器函数必须通过调用next方法才能执行,执行到yield标记符的时候暂停,此处yield后方的值就是当前next方法返回的value值,是下次调用next方法传入参数的入口值。
<script>
function *go(a){
console.log(1);
//此处的b用来供外界输入进来的
//这一行实现输入和输出,本次的输出放在yield后面,下次的输入放在yield前面
let b = yield a;
console.log(2);
let c = yield b;
console.log(3);
return c;
}
//生成器函数和普通的函数不一样,调用它的话函数并不会立刻执行
//它会返回此生成器的迭代器,迭代器是一个对象,每调用一次next就可以返回一个值对象
let it = go("a值");
console.log(it);//{}
//next第一次执行不需要参数,传参数没有意义
let r1 = it.next();
第一次调用next会返回一个对象,此对象有两个属性,一个是value就是yield后面那个值,一个是done表示是否迭代完成
console.log(r1);//{ value: 'a值', done: false }
let r2 = it.next('B值');
console.log(r2);//{ value: 'B值', done: false }
let r3 = it.next('C值');
console.log(r3);//{ value: 'C值', done: true }
</script>
2.生成器函数的参数传递
生成器函数第一次调用next方法不需要参数,有参数也没有意义,因为这时候前面没有yield的语句;以后的调用从上次的yield标志符传入,如果没有参数,则传入undefined:
<script>
function* foo(x) {
let a = 2 * (yield(x + 1));
let b = yield(a / 3);
return (x + a + b);
}
//没传参的情况
var it=foo(5);
console.log(it.next());//{ value: 6, done: false }
console.log(it.next());//{ value: NaN, done: false }
console.log(it.next());//{ value: NaN, done: true }
//传参数的情况:
var it1=foo(5);
//第一次next传参没意义
console.log(it1.next());//{ value: 6, done: false }
console.log(it1.next(6));//{ value: 4, done: false }
console.log(it1.next(7));//{ value: 24, done: true }
</script>
需要说明的是.next()方法返回的是yield右边表达式的值,.next(x)传递的参数是用参数值代替yield表达式的值给其他语句下一次调用next方法执行.
3.生成器函数的return影响
如果调用next方法后执行了return,那么生成器函数就结束了,后面即使有yield标记符也不会再执行了,此next方法返回对象的done键值为true.
<script>
function* generator(x) {
console.log('x', x);
let a = yield x;
return 10;
console.log('a', a);
let b = yield (x + 1) + a;
console.log('b',b);
let c= yield a + b;
console.log('a + b =', c);
return a + b;
}
generator(10);
let g = generator(10);
console.log(g.next());//{ value: 10, done: false }
console.log(g.next(1000));//{ value: 10, done: true }
console.log(g.next(50));//{ value: undefined, done: true }
console.log(g.next());//{ value: undefined, done: true }
</script>
对于for of循环函数,return的值不会被for of循环遍历到:
<script>
function* generator(x) {
console.log('x', x);
let a = yield x;
console.log('a', a);
let b = yield (x + 1) + a;
return "hello!"
console.log('b',b);
let c= yield a + b;
console.log('a + b =', c);
return a + b;
}
for(let item of generator(10)){
console.log(item)
}
</script>
for…of遍历函数的item相当于返回next方法的value值,只是最后的return值不返回而已。上面代码的结果为:
x 10
10
a undefined
NaN
4.try catch throw的执行
<script>
function *go(){
try{
yield;
}catch(e){
console.log("err",e)
}
}
let it=go();
it.next();
try{
it.throw("出错了");
}catch(e){
console.log("外部捕获",e)
}
//输出的结果是:"err 出错了"
</script>
5.yield运算优先级
yield运算符的优先级是最低的,其右方表达式的结果运算出来后在执行yield等待,除非用括号限定优先级,比如下列:
<script>
function* NinjaGenerator(action) {
const imposter = (yield "Hatori ") + action;
yield ("Yoshi (" + imposter + ") " + action);
}
const ninjaIterator = NinjaGenerator("skulk");
const result1 = ninjaIterator.next();
console.log(result1.value)//Hatori
const result2 = ninjaIterator.next("Hanzo");
console.log(result2.value)//Yoshi (Hanzoskulk) skulk
</script>
result1.value值为Hatori,而result2.value的值为Yoshi (Hanzoskulk) skulk如果把上面的代码第一个yield运算符的括号删掉,变成:
const imposter = yield "Hatori " + action;
那么result1.value值变为Hatori skulk,而result2.value的值为Yoshi (Hanzo) skulk