ECMAScript 6 笔记(21)- Generator 函数的语法(1)

一、简介

1.基本概念

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。Generator 函数有多种理解角度:
(1)语法上
a.状态机: 可以把 Generator 函数理解成是一个状态机,封装了多个内部状态。
b.遍历器对象生成函数: 执行 Generator 函数会返回一个遍历器对象,返回的遍历器对象可以依次遍历 Generator 函数内部的每一个状态。

(2)形式上
Generator 函数是一个普通函数,但是有两个特征:
a. function 关键字与函数名之间有一个星号 (ES6 没有规定,function 关键字与函数名之间的星号写在哪个位置,由于 Generator 函数仍是普通函数,所以一般星号紧跟在 function 关键字后面 )
b. 函数体内部使用 yield 表达式,定义不同的内部状态

2.Generator 函数的使用

上例定义了一个 Generator 函数 hellWorlGener,它内部有两个 yield 表达式,hello 和 world,即该函数有三个状态:hello,world 和 return 语句(结束执行)。

(1)Generator 函数的调用

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

(2)Generator 函数的执行

Generator 函数调用后反回了一个遍历器对象,必须调用遍历器对象的 next 方法,使得指针移向下一个状态。即,每次调用 next 方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式(或 return 语句)为止。
换言之,Generator 函数是分段执行的,yield 表达式是暂停执行的标记,而 next 方法可以恢复执行。

上例调用了四次 next 方法:
第一次调用:Generator 函数开始执行,直到遇到第一个 yield 表达式为止,next 方法返回一个对象,它的 value 属性就是当前 yield 表达式的值 hello,done 属性的值 false,表示遍历还没有结束。
...
第三次调用,Generator 函数从上次 yield 表达式停下来的地方,一直执行到 return 语句(如果没有return语句,就执行到函数结束)。next 方法返回对象的 value 属性就是紧跟在 return 语句后面的表达式的值(如果没有return语句,value值为 undefined),done 值为 true,表示遍历已结束。
第四次调用,此时 Generator 函数已经运行完毕,next 方法返回对象的 value 值为 undefined,done 值为 true,以后再调用 next 方法,返回的都是这个值。
总结:
调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的 next 方法,就会返回一个有着 value 和 done 两个属性的对象。value 属性表示当前的内部状态的值,即 yield 表达式后面的值;done 属性是一个布尔值,表示遍历是否结束。

二、yield 表达式

由于 Generator 函数返回的遍历器对象,只有调用 next 方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数,yield 表达式就是暂停标志。

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

(1) 遇到 yield 表达式,就暂停执行后面的操作,并将紧跟在 yield 后面的那个表达式的值,作为返回对象的 value 属性值。
(2) 下一次调用 next 方法时,再继续往下执行,直到遇到下一个 yield 表达式。
(3) 如果没有再遇到新的 yield 表达式,就一直运行到函数结束,直到 return 语句为止,并将 return 语句后面的表达式的值,作为返回对象的 value 属性值。
(4) 如果该函数没有 return 语句,则返回的对象的 value 属性值为 undefined。

注意:
yield 表达式后面的表达式,只有当调用 next 方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了 手动的 “惰性求值” 的语法功能。

2.yield 与 return

(1)相似之处
yield 与 return 的相似之处在于,都能返回紧跟在语句后面的那个表达式的值。
(2)区别
每次遇到 yield,函数暂停执行,下次再从该位置继续向后执行;而 return 语句不具备位置记忆的功能,一个函数里面,只能执行一次(或者说一个)return 语句,但 yield 表达式可以执行多次。
正常函数只能返回一个值,因为只能执行一次 return,Generator 函数可以返回一系列的值,因为可以有任意多个 yield。

3.注意

(1)yield 表达式只能使用在 Generator 函数里面,用在其他地方都会报错。
(2)yield 表达式如果用在另一个表达式之中,必须放在圆括号里面。
(3)yield 表达式用作函数参数或放在赋值表达式的右边,可以不加括号。

三、Generator 函数与 Iterator 接口的关系

任意一个对象的 Symbol.iterator 方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。
由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的 Symbol.iterator 属性,从而使得该对象具有 Iterator 接口。

Generator 函数执行后,返回一个遍历器对象。该对象本身也具有 Symbol.iterator 属性,执行后返回自身。

四、next 方法的参数

1.next 方法的参数的作用

yield 表达式本身没有返回值,或者说总是返回 undefined。next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值

上例中,定义了一个无限运行的 Generator 函数 f,如果 next 方法没有参数,每次运行到 yield 表达式,变量 reset 的值总是 undefined。当 next 方法带一个参数 true 时,变量 reset 就被重置为这个参数,下一轮循环就会从 -1 开始递增。

2.next 方法参数功能重要的语法意义

Generator 函数从暂停状态到恢复运行,它的上下文状态是不变的。通过 next 方法的参数,就有办法在 Generator 函数运行之后,继续向内部注入值。即,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。

注意:
由于 next 方法的参数表示上一个 yield 表达式的值,所以在第一次调用 next 方法时,传递参数是无效的。V8 引擎直接忽略第一次调用 next 方法时的参数,第二次以后参数才有效。语义上讲,第一个 next 方法用来启动遍历器对象,所以不用带参数。

五、for...of 与 Generator 函数

1.for...of 循环Generator 函数

for...of 循环可以自动遍历 Generator 函数时生成的 Iterator 对象,且此时不再需要调用 next 方法。

注意:
一旦 next 方法返回的对象 done 属性为 true,for...of 循环就会中止,且不包含该返回对象,所以上例的 return 语句返回的 5,不包括在 for...of 循环之中。

2.for...of 循环原生对象

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

另一种加遍历接口的写法是,将 Generator 函数加到对象的 Symbol.iterator 属性上面。

除了 for...of 循环以外,扩展运算符(...)、解构赋值和 Array.from 方法内部调用的都是遍历器接口。这意味,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数。

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复