generator函数

来源:互联网 发布:移动宽带端口限制 编辑:程序博客网 时间:2024/05/29 12:12

一、generator函数

1.对于generator函数的定义和相关的作用及语法

Generator函数是ES6提供的一种异步编程解决方案

Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。

执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。

形式上,Generator函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield语句,定义不同的内部状态.(yield语句不能再非generator函数中使用,否则会报错)

2.generator函数的简单使用

Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象

必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。

eg:

function* test() {  yield 'this is';  yield 'a test';  return 'ending';}var t = test();
t.next()// { value: 'this is', done: false }t.next()// { value: 'a test', done: false }t.next()// { value: 'ending', done: true }t.next()// { value: undefined, done: true }
当done的值为true以后以后调用next()方法返回相同的值

每次调用遍历器对象的next方法,就会返回一个有着valuedone两个属性的对象。value属性表示当前的内部状态的值,是yield语句后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

generator函数的“*”与方法之间可以有空格,也可以都没有,没有明确的规定(一般写在紧跟function以后,eg:function* test(){...})

yield语句:

  其实提供了一种可以暂停执行的函数。yield语句就是暂停标志。


(1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。

(3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

(4)如果该函数没有return语句,则返回的对象的value属性值为undefined


node命令行测试

function* test(){yield 'name = lcl'; yield 'age = 23'; return 'ending'}; 

var generator = test();

generator.next()    //{ value: 'name = lcl', done: false }


yield语句如果用在一个表达式之中,必须放在圆括号里面。

console.log('Hello' + yield 123); // SyntaxErrorconsole.log('Hello' + (yield 123)); // OK

yield 与 Iterator接口的关系:

任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。

由于Generator函数就是遍历器生成函数,因此可以把Generator赋值给对象的Symbol.iterator属性,从而使得该对象具有Iterator接口。

var myIterable = {};myIterable[Symbol.iterator] = function* () {  yield 1;  yield 2;  yield 3;};[...myIterable] // [1, 2, 3]
上面代码中,Generator函数赋值给Symbol.iterator属性,从而使得myIterable对象具有了Iterator接口,可以被...运算符遍历了。


next() 方法的参数

next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。

function* foo(x) {  var y = 2 * (yield (x + 1));  var z = yield (y / 3);  return (x + y + z);}var a = foo(5);a.next() // Object{value:6, done:false}a.next() // Object{value:NaN, done:false}a.next() // Object{value:NaN, done:true}var b = foo(5);b.next() // { value:6, done:false }b.next(12) // { value:8, done:false }b.next(13) // { value:42, done:true }

上面代码中,第二次运行next方法的时候不带参数,导致y的值等于2 * undefined(即NaN),除以3以后还是NaN,因此返回对象的value属性也等于NaN。第三次运行Next方法的时候不带参数,所以z等于undefined,返回对象的value属性等于5 + NaN + undefined,即NaN

如果向next方法提供参数,返回结果就完全不一样了。上面代码第一次调用bnext方法时,返回x+1的值6;第二次调用next方法,将上一次yield语句的值设为12,因此y等于24,返回y / 3的值8;第三次调用next方法,将上一次yield语句的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。

注意,由于next方法的参数表示上一个yield语句的返回值,所以第一次使用next方法时,不能带有参数。V8引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数。


使用generator函数 结合for...of 遍历普通对象的方法 

1、给普通对象添加Symbol.iterator(该iterator由generator函数返回)

2、使用for...of 遍历对象

下面是不同的两种方法给普通对象添加Symbol.iterator

利用for...of循环,可以写出遍历任意对象(object)的方法。原生的JavaScript对象没有遍历接口,无法使用for...of循环,通过Generator函数为它加上这个接口,就可以用了。

function* objectEntries(obj) {  let propKeys = Reflect.ownKeys(obj);  for (let propKey of propKeys) {    yield [propKey, obj[propKey]];  }}let jane = { first: 'Jane', last: 'Doe' };for (let [key, value] of objectEntries(jane)) {  console.log(`${key}: ${value}`);}// first: Jane// last: Doe

上面代码中,对象jane原生不具备Iterator接口,无法用for...of遍历。这时,我们通过Generator函数objectEntries为它加上遍历器接口,就可以用for...of遍历了。加上遍历器接口的另一种写法是,将Generator函数加到对象的Symbol.iterator属性上面。

function* objectEntries() {  let propKeys = Object.keys(this);  for (let propKey of propKeys) {    yield [propKey, this[propKey]];  }}let jane = { first: 'Jane', last: 'Doe' };jane[Symbol.iterator] = objectEntries;for (let [key, value] of jane) {  console.log(`${key}: ${value}`);}// first: Jane// last: Doe
 

generator函数的throw()方法的使用

generator函数的return()方法的使用

详细见:http://es6.ruanyifeng.com/#docs/generator


yield* 语句

如果在Generater函数内部,调用另一个Generator函数,默认情况下是没有效果的。

这个就需要用到yield*语句,用来在一个Generator函数里面执行另一个Generator函数。












0 0
原创粉丝点击