Generator函数

Generator函数

Generator函数可以理解为一个状态机,封装了多个内部对象。

执行Generator函数,会返回一个遍历器对象。也就是说,执行该函数,不会立即得到结果。

返回的遍历器对象,可以通过调用next方法,依次遍历Generator函数内部的每一个状态

Generator函数有两个特征:

1、function命令与函数名之间有一个*

2、在函数内部可以使用yield语句定义不同的内部状态

例:

function* gener(){   //有三个状态
    yeild 'hello';
    yeild 'world';
    return 'ending'
}

let result = gener();
console.log(result)    //此时得到的是一个遍历器对象
console.log(result.next())   //{value: 'hello' , done : false}
console.log(result.next())   //{value: 'world' , done : false}
console.log(result.next())   //{value: 'ending' , done : true}
console.log(result.next())   //{value: undefined , done : true}之后每次调用next方法都会得到这一结果

如上面的代码所示:执行Generator函数返回的是一个指向函数内部状态的指针对象,也就是遍历器对象。

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

也可以理解为:

Generator函数是分段执行的,yeild语句是暂停标志,而next方法可以恢复函数的执行

遍历器对象的next方法运行逻辑:

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

2、下一次调用next方法则继续向下执行,直到遇到下一条yield语句,继续执行步骤1

3、如果没有遇到yield语句,就一直向下运行,直到遇到return语句,并将return语句后面的表达式的值作为返回对象的value值,且将返回对象的done属性值变为true

4、如果该函数没有return语句,则返回{value:undefined , done:false}

yield语句后面的值,只有调用next方法,函数内部指针到达这一条yield语句时才执行。因此可以为JS提供惰性求值的功能

function* gen(){
    yield 123 + 456
}   //函数执行不会立即求值,只有调用函数执行产生的遍历器对象的next方法,内部指针到达这一句时,yeild语句后面的表达式才会求值

yeild语句和return语句的异同:

同:

都能返回紧跟在表达式后面的值

异:

一个Generator函数中可以有很多个yeild语句,而return语句只能有一个

每次遇到yeild语句函数就停止执行,下一次从该位置继续向后执行,yeild语句具有记忆功能。

return语句不具备记忆功能,一个函数中执行了return语句就以为着结束了

return语句会将遍历器对象执行next方法返回对象的done属性改为true,return语句后面的yield语句都不会生效的

注意:Generator函数中可以没有yeild语句,但是yeild语句只能出现在Generator函数中

例:

如果是普通函数,函数执行就会立即打印‘Hello World’

function* say(){
    console.log('Hello World')
}
let gener = say();
setTimeout(function(){
    gener.next()   //此时才会打印'Hello World'
},2000)

Generator函数与Iterator接口的关系

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

同时,遍历器对象本身也有Symbol.iterator方法,执行后返回自身

测试一下:

function* gen(){
    //一些代码
}
let g = gen();
console.log(g[Symbol.iterator]() === g)   //true

next()方法的参数

函数内部,yeild语句是没有返回值的,是undefined.

每次调用遍历器对象的next方法时,可以传入一个参数,该参数被当作上一条yeild语句的返回值,当函数内部代码依赖上一条yeild语句的返回结果时,在next()方法中传递参数时十分有用的

遍历器对象第一次调用next方法不需要带参数,即使有也会被忽略,因为第一次调用next方法是用来启动遍历器对象,使指针指向第一个yeild语句的

例:

function* gen(){
    for(var i = 0 ; true ; i++){
        let reset = yield i;
        if(reset) {i = -1}
    }
}
let g = gen();
console.log(g.next())	//{value:0 , done: false}
console.log(g.next())	//{value:1 , done: false}
console.log(g.next())	//{value:2 , done: false}
console.log(g.next())	//{value:3 , done: false}
console.log(g.next(true))	//{value:0 , done: false}
console.log(g.next())	 //{value:1 , done: false}
console.log(g.next(true))	//{value:0 , done: false}

上面代码可以得出:不传参数:

函数内部的reset依赖yield i 的返回结果,不传参数的情况下,永远是undefined,不会走入if条件中

传了参数true,改变reset的值为true,将i改为-1,因此返回的i又从-1开始递增

例2:

function* gen(x){
    var y = 2 * (yield(x + 1));
    var z = yield (y / 3);
    return (x + y + z)
}
const g = gen(5);
console.log(g.next())    //{value : 6 , done:false}
console.log(g.next())	//{value : NaN , done:false}
console.log(g.next())	//{value : NaN , done:true}

const g = gen(5);
console.log(g.next())    //{value : 6 , done:false}
console.log(g.next(12))	//y=2*12  {value : 8 , done:false}
console.log(g.next(10))	//y=24 z= 10 {value : 39, done:true}

for…of…循环

for…of…循环可以自动遍历Generator函数,且不在需要调用next方法

function* gen(){
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    return 5;
}

for( i of gen()){
    console.log(i) // 1,2,3,4
}

一旦next方法的返回对象的done属性为true,循环就会终止,且不包含那一项,所以return的5不包含在for…of…循环中

我们都知道,for…of…循环,拓展运算符,解构赋值和Array.from方法内部调用的都是遍历器接口

因此,他们都可以将Generator函数返回的Iterator对象作为参数传递进去

function* gen(){
    yield 1;
    yield 2;
    yield 3;
    return 4;
    yield 5;
}
const g = gen();
console.log([...g])  //[1,2,3]l
console.log(Array.from(g))   //[1,2,3]
let [x,y,z] = g;    //x : 1 , y : 2 , z : 3
for(i of g){
    console.log(g)   //1,2,3
}
Generator函数为对象部署Iterator接口
function* objectEntries(obj) {
    let propKeys = Reflect.ownKeys(obj);
    for(let propKey of propKeys) {
        yield [propKey , obj[propKeys]]
    }
}
let object = {
    name: 'Gary',
    age: 18
}
for(let [key , value] of objectEntries(object)){
    console.log(`${key} : ${value}`)  // name: 'Gary'   age : 18
}

方法二、将Generator函数加到对象的Symbol.iterator属性

function* objectEntries(obj) {
    let propKeys = Reflect.ownKeys(obj);
    for(let propKey of propKeys) {
        yield [propKey , obj[propKeys]]
    }
}
let object = {
    name: 'Gary',
    age: 18
}
object[Symbol.iterator] = objectEntires
for(let [key , value] of object){
    console.log(`${key} : ${value}`)  // name: 'Gary'   age : 18
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值