es6-Generator

153 阅读4分钟

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"