Generator函数
一、作用
提供异步编程方案
二、特点
(1)状态机。封装了多个状态,通过yield表达式进行定义
function* g(){ while(true){ yield true; yield false; } } const obj = g(); console.log(obj.next()); // {value: true, done: false} console.log(obj.next()); // {value: false, done: false} console.log(obj.next()); // {value: true, done: false} console.log(obj.next()); // {value: false, done: false}
(2)是一个遍历器对象生成函数(不理解可以看遍历器),总是返回一个遍历器,ES6规定这个遍历器是 Generator 函数的实例,也继承了 Generator 函数的prototype
对象上的方法。但是this的指向为window,可以通过 call() 修改 this 的指向。
function* g(){ console.log("start"); this.a = 1; yield this.b = 2; yield this.c = 3; } g.prototype.hello = function () { console.log('a'); } const obj = g(); console.log(obj instanceof g); // true (obj是g的实例) obj.hello(); // a (实例继承了prototyped上的方法,这个特点跟我们自己创建一个class的效果是一样的) obj.next(); // 至少要调用一次,不然g()里面的代码都没有执行; console.log(obj.a); // undefined console.log(window.a); // 1 //修改 this 的指向 const newObj = g.call(g.prototype); newObj.hello(); // a newObj.next(); console.log(newObj.a); // 1 console.log(newObj.b); // 2 console.log(newObj.c); // undefined (因为还没执行到this.c = 3)
二、语法
(1)function 和 函数名之间有一个星号,能区别于其他的普通函数
(2)函数体内部使用yield表达式,起到暂停的作用。(yield只能在Generator函数里使用,普通函数使用会报错)yield表达式如果用在另一个表达式之中,必须放在圆括号里面。
如果在一个Generator函数里面要调用另一个Generator函数,要写成 yield* F() , 等同于调用了for....of.....
function* P(){ yield 'a'; yield 'b'; yield 'c'; } function* S(){ yield 'd'; yield* P(); yield 'e'; } function* S1(){ yield 'd'; for(let value of P()){ yield value; } yield 'e'; } const s = S(); console.log(s.next()); // {value: "d", done: false} console.log(s.next()); // {value: "a", done: false} console.log(s.next()); // {value: "b", done: false} console.log(s.next()); // {value: "c", done: false} console.log(s.next()); // {value: "e", done: false} console.log(s.next()); // {value: undefined, done: true} const s1 = S1(); console.log(s1.next()); // {value: "d", done: false} console.log(s1.next()); // {value: "a", done: false} console.log(s1.next()); // {value: "b", done: false} console.log(s1.next()); // {value: "c", done: false} console.log(s1.next()); // {value: "e", done: false} console.log(s1.next()); // {value: undefined, done: true}
(3)执行到return之后,遍历就结束了,即使后面还有yield表达式也是无效。
function* gerenator(arr){ for (let value of arr) { yield value; } return 'ending'; yield 111; } const g = gerenator(['a','b']); console.log(g.next()); //{value "a", done: false} console.log(g.next()); //{value "b", done: false} console.log(g.next()); //{value "ending", done: true} console.log(g.next()); //{value undefined, done: true}
(4)next方法的参数会被当作上一个yield
表达式的返回值。
function* generator(arr){ for(let item of arr){ let bool = yield item; if(bool){ return; } } } const arr = ['a','b','c','d']; const g = generator(arr); console.log(g.next()); // {value: 'a', done: false} console.log(g.next()); // {value: 'b', done: false} console.log(g.next(true)); // {value: undefined, done: true} console.log(g.next()); // {value: undefined, done: true}
三、API
1.Generator.prototype.throw()
(1)作用:捕获函数体外抛出的错误
(2)必须至少执行一次next()之后才能进行捕获
(3)捕获后,会附带执行一次next方法
function* generator(){ try { yield 'a'; yield 'b'; yield 'c'; } catch (err) { console.log(`内部捕获:${err}`); } yield 'zxn'; yield 'zxn1'; } const g = generator(); try { console.log(g.next()); // {value: 'a', done: false} console.log(g.throw("第一次出错")); // 内部捕获:第一次出错 {value: 'zxn', done: false} console.log(g.next()); // {value: 'zxn1', done: false} console.log(g.throw("第二次出错")); // 外部捕获:第二次出错 } catch (err) { console.log(`外部捕获:${err}`); }
2.Generator.prototype.return()
(1)可以返回特定的值,并结束遍历器(如果有try......finally,会提前进入finally中)
function* generator(){ try { yield 'a'; yield 'b'; yield 'c'; } catch (err) { console.log(`内部捕获:${err}`); } finally { yield 'F1'; yield 'F2'; } yield 'zxn'; yield 'zxn1'; } const g = generator(); console.log(g.next()); // {value: 'a', done: false} console.log(g.return('结束,提前进入finally')); // {value: 'F1', done: false} console.log(g.next()); // {value: 'F2', done: false} console.log(g.next()); // {value: "结束,提前进入finally", done: true} console.log(g.next()); // {value: undefined, done: false}
3.Generator.prototype.next()
只有开始调用next()方法的时候,才会开始执行遍历器里面的代码,遇到yield或return就会返回。执行完return之后遍历器就结束了
四、应用
(1)部署Iterator接口
const obj = {id: 1, name: "zheng", con: "content"}; obj[Symbol.iterator] = function* (){ for(let value in this){ yield this[value]; } return "ending"; } for(let value of obj){ console.dir(value); // 1 zheng content }
(2)异步操作
请查看《异步编程》