本系列属于阮一峰老师所著的ECMAScript 6 入门学习笔记javascript
Generator函数是ES6提供的一种异步编程解决方案。形式上,Generator函数是一个普通函数,可是有两个特征:function
关键字与函数名之间有一个星号;函数体内部使用yield
表达式,定义不一样的内部状态(yield
表意产出)。java
function* helloWorldGenerator(){ yield 'hello' yield 'world' return 'ending' } var hw = helloWorldGenerator() // Generator函数和普通函数同样调用,可是调用后函数并不执行,返回一个指向内部状态的指针对象,即遍历器对象(Iterator Object) hw.next() // {value: 'hello',done:false} hw.next() // {value: 'world',done:false} hw.next() // {value: 'ending',done:true} hw.next() // {value: undefined,done:true} // 每次调用next方法,执行Generator函数,依次返回yeild表达式的值,执行到return语句(若没有return语句就执行到结束)
Generator函数返回遍历器对象,只有调用next
方法才会遍历下一个状态,yield
表达式就是暂停函数执行的暂停标志。es6
Generator函数能够不用yield
表达式,这时就变成一个单纯的暂缓执行函数编程
function* f(){ console.log('done!') } var generator = f() // 函数不会当即执行,只有调用next方法才会执行 setTimeout(function(){ generator.next() },2000)
yield
表达式若是在另外一个表达式中,必须放在圆括号内数组
function* demo(){ console.log('Hello' + yield) // SyntaxError console.log('Hello' + (yield)) // OK } // yield表达式用做函数参数或放在赋值表达式的右边,能够不加括号 function* demo(){ foo(yield 'a',yield 'b') // OK let input = yield // OK }
任何一个对象的Symbol.iterator
方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。因为Generator函数就是遍历器生成函数,所以能够把Generator函数赋值给Symbol.iterator
属性,从而使得该对象具备iterator接口。数据结构
var myIterator = {} myIterator[Symbol.iterator] = function* (){ yield 1 yield 2 yield 3 } [...myIterator] // [1,2,3]
yield
表达式自己没有返回值,或者说每次都返回undefined。next
方法能够带一个参数,该参数会被当作上一个yield
表达式的返回值异步
function* f() { for(var i = 0; true; i++) { var reset = yield i; if(reset) { i = -1; } } } var g = f(); g.next() // { value: 0, done: false } g.next() // { value: 1, done: false } g.next(true) // { value: 0, done: false } // 每次运行到yield表达式,变量reset总被赋值undefined,当next带上参数以后,遍历reset就被赋值为true
for...of
循环能够自动遍历Generator函数生成的Iterator
对象,且不用再调用next
方法异步编程
function* foo(){ yield 1 yield 2 yield 3 yield 4 return 5 } for(let v of foo()){ console.log(v) } // 1 2 3 4 // for...of循环遇到next方法返回对象的done为true就会停止,因此return返回的5不在循环之中 // 除了for...of循环外,拓展运算符...、解构赋值和Array.from均可以将Generator函数返回的Iterator对象做为参数 function* numbers () { yield 1 yield 2 return 3 yield 4 } // 拓展运算符 [...numbers()] // [1,2] // Array.from Array.from(numbers()) // [1,2] // 解构赋值 let [x,y] = numbers() x // 1 y // 2 // for...of循环 for(let n of numbers()){ console.log(n) // 1 2 }
Generator函数返回的遍历器对象有一个throw
方法,能够在函数体外抛出错误,而后在函数体内捕获函数
var g = function* (){ try{ yield }catch(e){ console.log('内部捕获',e) } } var i = g() i.next() // throw方法能够接收一个参数,改参数会被catch语句接收,建议抛出Error对象实例 try { i.throw('a') i.throw('b') }catch(e){ console.log('外部捕获',e) } // 内部捕获 a // 外部捕获 b // throw方法被捕获后会附带执行下一条yield表达式,也就是说会附带执行一次next方法
return
方法返回给定的值,而且终结遍历Generator函数学习
function* gen(){ yield 1 yield 2 yield 3 } var g= gen() g.next() // {value:1,done:false} g.return('foo') // {value:'foo',done:true} g.next() // {value:undefined,done:true} // 若是Generator函数内部有try...finally代码块,那么return方法会推迟到finally代码执行完以后再执行 function* numbers(){ yield 1 try{ yield 2 yield 3 }finally{ yield 4 yield 5 } yield 6 } var g = numbers() g.next() // {value:1,done:false} g.next() // {value:2,done:false} g.return(7) // {value:4,done:false} g.next() // {value:5,done:false} g.next() // {value:7,done:true}
在Generator函数内调用另外一个Generator函数,默认状况下是没有效果的,这时候就要用到yield*
表达式
function* inner(){ yield 'hello!' } function* outer1(){ yield 'open' yield inner() yield 'close' } var gen = outer1() gen.next().value // 'open' gen.next().value // 返回一个遍历器对象 gen.next().value // 'close' function* outer2(){ yield 'open' yield* inner() yield 'close' } var gen = outer2() gen.next().value // 'open' gen.next().value // 'hello' gen.next().value // ’close' function* concat(iter1,iter2){ yield* iter1 yield* iter2 } // 等同于 function* concat(iter1,iter2){ for(var value of iter1){ yield value } for(var value of iter2){ yield value } } function* gen(){ yield* ['a','b','c'] } gen().next() // {value:'a',done:false} // 若不加星号返回的是整个数组,加了就表示返回的是数组的遍历器对象。实际上任何具备Iterator接口的数据结构均可以被yield遍历 function *foo() { yield 2; yield 3; return "foo"; } function *bar() { yield 1; var v = yield *foo(); console.log( "v: " + v ); yield 4; } var it = bar(); it.next() // {value: 1, done: false} it.next() // {value: 2, done: false} it.next() // {value: 3, done: false} it.next(); // "v: foo" // {value: 4, done: false} it.next() // {value: undefined, done: true} //被代理的Generator函数foo有return语句,那么就会向代理它的Generator函数bar返回数据,而且继续执行next方法
let obj = { * myGeneratorMethod(){ ... } }
// 生成空对象,使用call方法绑定Generator函数内部的this function* F(){ this.a = 1 yield this.b = 2 yield this.c = 3 } var obj = {} var f = F.call(obj) // 调用三次next方法完成F内部全部代码的运行,将全部内部属性绑定在obj对象上 f.next() // {value:2,done:false} f.next() // {value:3,done:false} f.next() // {value:undefined,done:true} // obj对象编程了F的实例 obj.a // 1 obj.b // 2 obj.c // 3 // 将F改为构造函数,能够执行new命令 function* gen(){ this.a = 1 yield this.b = 2 yield this.c = 3 } function F(){ return gen.call(gen.prototype) } var f = new F() f.next() // {value:2,done:false} f.next() // {value:3,done:false} f.next() // {value:undefined,done:true} f.a // 1 f.b // 2 f.c // 3