Generator 语法
一直觉得这个很难理解,每次看es6,看到这里直接跳过哈哈哈哈,这几天准备认真看下,至少基本的知识要理解
基本语法
Generator是一个函数,它返回一个遍历器对象,它有两个特征,function 关键字 和 函数名之间有个星号,函数体内使用yield表达式,定义内部状态
function* helloGenrator(){
yield 'hello';
yield 'world';
return 'ending'
}
var hw = helloGenrator()
调用函数后并不会执行,而是返回一个指向内部状态的指针对象,就是遍历器对象,(Iterator Ojbect)
必须调用next方法才会使指针指向下一个状态,直到遇到yield 表达式或者return语句 调用如下图
next 方法返回一个对象,该对象有两个属性,value 和 done,value 是内部状态的值,done 是布尔值,表示是否遍历结束
yield 表达式可以记录当前指行的位置,而return不能,
next 方法的参数
首先yield 表达式本身没有返回值 我们看下
function* gen(){
let res = yield 111
console.log(res)
}
let g = gen()
g.next()
g.next()
//输出res 是undefined
next 方法可以带一个参数,该参数就会被当做上一个yield表达式的返回值
function* foo(x){
let res = yield x
yield res
}
var g = foo(3)
g.next() // {value:3,done:false}
g.next() // {value:undefined,done:false}
g.next() // {value:undefined,done:true}
var g = foo(3)
g.next() // {value:3,done:false}
g.next(2) // {value:2,done:false}
g.next() // {value:undefined,done:true}
由此,我们可以看到,当传参2 的时候,代表上一个yield表达式的值,即res是2 然后yield res 就要输出{value:2,done:false}
for...of 循环
因为Generator函数返回的是遍历器对象,所以for...of可以进行遍历
function* foo(){
yield 1;
yield 2;
yield 3;
return 6;
}
for(let v of foo()){
console.log(v)
}
// 1 2 3
用Generator 实现斐波那契
function* fibonacci(){
let [prev,curr] = [0,1]
for(;;){
yield curr
[prev,curr] = [curr,prev+curr]
}
}
for(let v of fibonacci()){
if(v>2000){
break;
}
console.log(v)
}
Generator.prototype.throw()
- Generator函数返回的遍历器对象,都有个throw 方法,可以在函数体外抛出错误,然后再Generator函数体内捕获
var g = function* (){
try{
yield;
}catch(e){
console.log('内部错误',e)
}
}
var i = g()
i.next();
try{
i.throw('a')
i.throw('b')
}catch(e){
console.log('外部错误',e)
}
// 内部错误 a 第一个错误被Generator内部捕获
// 外部错误 b 因为Generator内部catch 已经执行一次了,所以这个错误被外部捕获
- 遍历器对象的throw方法,可以接受一个参数,该参数会被catch接收
- 如果函数内部没有try...catch语句,那么throw方法抛出的错误将被外部try...catch捕获
var g = function* (){
yield;
}
var i = g()
i.next()
try{
i.throw('a')
}catch(e){
console.log('捕获错误',e)
}
//捕获错误 a
如果内部外部都没有try...catch 那么程序直接报错中止
var g = function* (){
yield;
}
var i = g()
i.next()
i.throw('a')
// Uncaught a
-
generator 内部要想捕获错误,必须先执行一次next
-
i.throw 方法执行后,会附带执行一次yield语句,不影响下一次遍历(这里和promise的一个特性一样?)
-
generator 内部 throw抛出一个错误,外部可以捕获到
var g = function* (){
throw('a')
yield;
}
try{
var i = g()
i.next()
}catch(e){
console.log(e)
}
// a
- 一旦 Generator 执行过程中抛出错误,且没有被内部捕获,就不会再执行下去了。如果此后还调用next方法,将返回一个value属性等于undefined、done属性等于true的对象
Generator.prototype.return()
- return 方法可以接收一个值,代表可以返回的值,并终结遍历器执行
var g = function* (){
yield 1;
yield 2
}
var g1 = g()
g1.next() // {value:1,done:false}
g1.return(2) // {value:2,done:true}
g1.next() // {value:undefined,done:true}
- 如果return 没有参数,那么返回的值undefined
- 如果 Generator 函数内部有try...finally代码块,且正在执行try代码块,那么return()方法会导致立刻进入finally代码块
var g = function* (){
yield 1;
try{
yield 3;
}finally {
yield 4;
}
yield 5;
}
var g1 = g()
g1.next() // {value:1,done:false}
g1.return(2) // {value:2,done:true}
g1.next() // {value:undefined,done:true}
g1.next() // {value:1,done:false}
g1.next() // {value:3,done:false}
g1.return() // {value:5,done:false}
yield* 表达式
Generator 内嵌套 Generator函数,我们可以用yield* 表达式, 它返回一个遍历器对象
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
yield 'a';
yield 'b';
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}
for (let v of bar()){
console.log(v);
}
// "x"
// "a"
// "b"
// "y"