ES6 Generator

  • Generator生成器是ES6标准引入的新的数据类型,一个生成器看上去像是一个函数但可以返回多次。

  • Generator生成器是ES6引入的,用于异步编程,最大特点是可以交出函数的执行权(暂停执行),暂停执行主要是通过 yield 来实现。

  • Generator生成器是一个状态机,封装了多个内部状态。

  • Generator生成器本质上是一个封装的异步任务

普通函数

普通函数调用时会先传入参数然后返回结果,函数执行过程中,如果没有遇到return语句,函数的控制权是无法交回给被调用的代码的。

//普通函数
function fn(){
    console.log("normal function");
}
fn();//normal function

生成器

生成器定义

//生成器
function* generator(){
    yield 1;
    yield 2;
    yield 3;
    return undefined;
}

let iter= generator();//创建函数句柄并未实际执行
console.log(iter);//iter{<suspended>}

let obj1 = iter.next();
console.log(obj1);//{value: 1, done: false}

let obj2 = iter.next();
console.log(obj2);//{value: 2, done: false}

let obj3 = iter.next();
console.log(obj3);//{value: 3, done: false}

let obj4 = iter.next();
console.log(obj4);//{value: undefined, done: true}

注意:每次调用next()方法会返回一个对象,表示当前阶段的信息,对象包括两个属性,value属性表示返回的值,done布尔值表示函数执行是否结束。

  • function关键字与函数名之间有一个星号*

星号的作用是用于标识不同于普通函数,表示生成器是可以暂停执行的。

  • 生成器的函数体内部使用yield语句可以定义不同的内部状态

所谓的状态实际就是数据,函数内部的状态实际是指函数内部的值,在不同时函数内部的值是不同的。

生成器与普通函数的区别

生成器与普通函数的区别在于,生成器由function*定义,它除了return返回语句外,还可以使用yield命令返回多次。

//生成器
function* generator(){
    yield 1;
    yield 2;
    yield 3;
    return undefined;
}

let iter= generator();
while((item = iter.next()).done === false){
    console.log(item.value);
}

生成器函数表达式

可以通过函数表达式来创建生成器,只需要在function关键字和小括号之间添加星号*即可。

let generator = function* (items){
  for(let i=0; i<items.length; i++){
      yield items[i];
  }
};
let iterator = generator([1,2,3]);
for(let item of iterator){
    console.log(item);
}

生成器对象

由于生成器本身就是函数,因此可以将其添加到对象中。

使用ES5对象字面量通过函数表达式创建生成器

var obj = {
    generator:function *(items){
        for(let i=0; i<items.length; i++){
            yield items[i];
        }
    }
};
let iter = obj.generator([1,2,3]);
for(let item of iter){
    console.log(item);
}

使用ES6函数方法简写方式创建生成器,只需在函数名前添加星号。

let obj = {
  *generator(items){
      for(let i=0; i<items.length; i++){
          yield items[i];
      }
  }
};

let iter = obj.generator([1, 2, 3]);
for(let item of iter){
    console.log(item);
}

利用生成器作为状态机

let state = function* (){
    while(true){
        yield 0;
        yield 1;
        yield 2;
    }
};

let status = state();
console.log(status.next().value);//0
console.log(status.next().value);//1
console.log(status.next().value);//2

yield 关键字

yield是生成器内部的暂缓执行的标识,yield只能配合生成器使用,普通函数中使用则会报错。

function* generator(items){
    items.forEach((item)=>{
       yield item;//Uncaught SyntaxError: Unexpected identifier
    });
}

yield关键字可以返回任何值或表达式,可以通过生成器批量给迭代器添加元素。

function* generator(items){
    for(let i=0; i<items.length; i++){
        yield items[i];
    }
}
let iter = generator([1,2,3]);
for(let item of iter){
    console.log(item);// 1 2 3
}

yield只能原封不动地返回右边运算后的值

let generator = function* (){
    yield 0;
    yield 1+1;
    yield gen();//yield只能原封不动的返回右边运算后的值
};
let gen = function* (){
    yield 10+1;
    yield 20;
};
var iter = generator();
console.log(iter.next().value);//0
console.log(iter.next().value);//2
console.log(iter.next().value);//gen {<suspended>}

使用yield*可以让其自动遍历该对象

let generator = function* (){
    yield 0;
    yield 1+1;
    yield* gen();
};
let gen = function* (){
    yield 10+1;
    yield 20;
};
var iter = generator();
console.log(iter.next().value);//0
console.log(iter.next().value);//2
console.log(iter.next().value);//11

yield表达式的返回值

let generator = function* (arg){
    console.log(arg);
    let ret = yield arg;
    console.log(ret);
};
let iter = generator(1);

console.log(iter.next());
// 1
// {value: 1, done: false}

console.log(iter.next());
// undefined
// {value: undefined, done: true}

console.log(iter.next(2));
//{value: undefined, done: true}

生成器返回值

执行生成器会返回一个遍历器(迭代器Iterator)对象,也就是说生成器除了是状态机还是一个遍历器生成函数,根据返回的遍历器对象可依次遍历生成器内部的每个状态。

调用生成器后函数并不执行,返回的不是函数运行结果,而是指向内部引用状态的指针对象,也就是遍历器对象。

因此必须调用遍历器对象的next()方法,使得指针移向下一个状态。也就是说,每次调用next()方法内部指针会从函数头部或上次停止的位置开始执行,直到遇到下一个yield表达式或return语句为止。换言之,生成器是分段执行的,yield表达式只是暂停的标志,next()方法可以恢复执行。

实现了迭代器接口的对象都可以使用for...of实现遍历

function* generator(){
    let i = 0;
    yield ++i;
    yield ++i;
    yield ++i;
    return undefined;
}
//生成器函数执行后返回的是一个迭代器对象
let iterator = generator();
//迭代器可使用for...of遍历
for(let item of iterator){
    console.log(item);//1 2 3
}

迭代器

迭代器是一种特殊的对象,具有专门为迭代过程设计的专用接口。

  • 所有的迭代对象都具有一个next()方法,每次调用时都回返回一个结果对象。结果对象包含两个属性,一个是value表示下一个将要返回的结果,另一个是done表示一个布尔类型的值。但没有更多可返回数据时done会返回true
  • 迭代器还会保存一个内部指针,用来指向当前集合中值的位置,每次调用next()方法都回返回下一个可用的值。

如果在最后一个值返回后再调用next()方法返回的对象中属性done的值将为true,属性value则包含迭代器最终返回的值,这个返回值不是数据集的一部分,它会函数的返回值类似,是函数调用过程中最后一次给调用者传递信息的方法,如果没有相关数据则返回undefined

例如:ES5中创建迭代器的方式

//ES5创建迭代器
function createIterator(items){
    var i = 0;
    return {
      next:function(){
          var done = i >= items.length;
          var value = done ? undefined : items[i++];
          return {value:value, done:done};
      }
    };
}

var iter = createIterator([1, 2, 3]);
console.log(iter);//{next: ƒ}

console.log(iter.next());//{value: 1, done: false}
console.log(iter.next());//{value: 2, done: false}
console.log(iter.next());//{value: 3, done: false}
console.log(iter.next());//{value: undefined, done: false}

生成器使用

  • 通过yield标识位和next()方法调用,可以实现函数的分段执行。
  • yield 命令是异步不同阶段的分界线,是生成器暂停执行的标识。
  • yield只能配合生成器使用
  • 生成器执行时需使用next()方法,next()可以理解为启动方法,作用是分阶段执行生成器,每次调用next()方法会返回一个对象,该对象表示生成器当前阶段的信息。

例如:使用普通函数获取斐波拉契数列

function fib(max){
    let tmp, x = 0, y = 1, ret = [];
    while(ret.length < max){
        [x, y] = [y, x+y];
        ret.push(y);
    }
    return ret;
}
console.log(fib(5));// [1, 2, 3, 5, 8]

例如:使用生成器产生斐波拉契数列

//定义生成器
function* fib(max){
    let tmp, count = 0, x = 0, y = 1;
    while(count < max){
        yield x;
        [x, y] = [y, x+y];
        count++;
    }
    return;
}
let gen = fib(5);//创建生成器对象
// 生成器的next()方法会执行函数体内的代码
// 每次遇到yield就会返回一个对象{value:x, done:bool}并暂停
// 返回的value是yield的返回值,done则表示生成器是否已经执行结束。
// 如果done的值为true,则此时value就是生成器函数体内return的值。
console.log(gen.next());//{value: 0, done: false}
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next());//{value: 2, done: false}
console.log(gen.next());//{value: 3, done: false}
console.log(gen.next());//{value: undefined, done: true}

生成器的作用

由于生成器可以在执行过程中多次返回,所以看上其就像一个可以记住执行状态的函数。利用这一点,编写生成器可以实现需要面向对象才能实现的功能。

例如:使用对象来保存状态

let fib = {
    x:0,
    y:1,
    count:0,
    max:5,
    next:function(){
        let result = this.x;
        let tmp = this.x + this.y;

        this.x = this.y;
        this.y = tmp;
        if(this.count < this.max){
            this.count++;
            return result;
        }else{
            return undefined;
        }
    }
};
console.log(fib.next());//0
console.log(fib.next());//1
console.log(fib.next());//1
console.log(fib.next());//2
console.log(fib.next());//3
console.log(fib.next());//undefined
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 简介 基本概念 Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍...
    呼呼哥阅读 1,068评论 0 4
  • 本文作者就是我,简书的microkof。如果您觉得本文对您的工作有意义,产生了不可估量的价值,那么请您不吝打赏我,...
    microkof阅读 23,720评论 16 78
  • 1.基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细...
    lhdoeo阅读 350评论 0 1
  • 在此处先列下本篇文章的主要内容 简介 next方法的参数 for...of循环 Generator.prototy...
    醉生夢死阅读 1,437评论 3 8
  • Generator 函数的语法 简介 基本概念 Generator 函数是 ES6 提供的一种异步编程解决方案,语...
    站在大神的肩膀上看世界阅读 4,164评论 0 6