## Generator函数语法
### 简介
#### 基本概念
`Generator` 函数是ES6提供的一种异步编程解决方案,语法行为与普通函数完全不同。
对于 `Generator` 函数有多种理解角度,从语法上,可以把它理解成一个状态机,封装了多个内部状态。
执行 `Generator` 函数会返回一个遍历器对象,所以它除了是状态机,还是一个遍历器对象生成函数,返回的遍历器对象可以依次遍历 `Generator` 函数内部的每一个状态。
形式上,`Generator` 函数是一个普通函数,但有两个特征:一是 `function` 命令与函数名之间有一个星号;二是函数体内部使用 `yield` 语句定义不同的内部状态。(`yield`:产出)
```js
function* helloGenerator () {
yield 'hello'
yield 'Generator'
return 'ending'
}
let hw = helloGenerator()
```
`Generator` 函数的调用方法与普通函数一样,不过的是,它并不会马上执行,返回的也不是运行结果,而是一个指向内部状态的指针对象。
接下来,必须调用遍历器对象的 `next` 方法,使得指针向下一个状态移动 。
```js
hw.next() // { value: 'hello', done: false }
hw.next() // { value: 'Generator', done: false }
hw.next() // { value: 'ending', done: true }
hw.next() // { value: undefined, done: true }
```
#### yield表达式
`Generator` 函数返回的遍历器对象只有调用了 `next` 方法才会遍历到下一个内部状态,所以实际上它提供了一种可以暂停执行的函数,`yield` 语句就是暂停标志。
#### 与Iterator接口的关系
由于 `Generator` 函数就是遍历器生成函数,因此可以把 `Generator` 赋值给对象的 `Symbol.iterator` 属性,从而使得对象具有 `Iterator` 接口。
```js
let myIterable = {}
myIterable[Symbol.iterator] = function* () {
yield 1
yield 2
yield 3
}
[...myIterable] // [1, 2, 3]
```
可以被扩展运算符(...)遍历,从而生成一个数组。
#### next方法的参数
`next` 方法可以带有一个参数,该参数会被当做上一条 `yield` 语句的返回值
### for...of循环
`for...of` 循环可以自动遍历 `Generator` 函数生成的 `Iterator` 对象,此时不再需要调用 `next` 方法。
```js
function* foo () {
yield 1
yield 2
yield 3
yield 4
return 5
}
for (let v of foo()) {
console.log(v)
}
// 1 2 3 4 5
```
利用 `for...of` 循环,可以写出遍历任意对象的方法。原生的JavaScript对象没有遍历器接口,无法使用 `for...of` 循环,通过 `Generator` 函数为它加上接口就可以使用了。
```js
function* objectEntries(obj) {
let proKeys = Reflect.ownKeys(obj)
for (let proKey of proKeys) {
yield [proKey, obj[proKey]]
}
}
let jane = { first: 'Jane', last: 'Doe' }
for (let [key, value] of objectEntries(jane)) {
console.log(`${key}: ${value}`)
}
// first: Jane
// last: Doe
```
还有另外一种写法:将 `Generator` 函数加到对象的 `Symbol.iterator` 属性上。
```js
function* objectEntries () {
let propKeys = Object.keys(this)
for (let propKey of propKeys) {
yield [propKey, this[propKey]]
}
}
let jane = { first: 'Jane', last: 'Doe' }
jane[Symobl.iterator] = objectEntries
for (let [key, value] of jane) {
console.log(`${key}: ${value}`)
}
// first: Jane
// last: Doe
```
### Generator.prototype.throw()
`Generator` 函数返回的遍历器对象都有一个 `throw` 方法,可以在函数体外抛出错误,然后在 `Generator` 函数体内捕获。
```js
let g = function* () {
try {
yield
} catch (e) {
console.log('内部捕获', e)
}
}
let i = g()
i.next()
try {
i.throw('a')
i.throw('b')
} catch (e) {
console.log('外部捕获', e)
}
// 内部捕获 a
// 外部捕获 b
```
### Generator.prototype.return()
`Generator` 函数返回的遍历器对象还有一个 `return` 方法,可以返回给定的值。并终结 `Generator` 函数的遍历。
```js
function* gen () {
yield 1
yield 2
yield 3
}
let g = gen()
g.next() // { value: 1, done: false }
g.return('foo') // { value: 'foo', done: true }
g.nextr() // { value: undefined, done: true }
```
### yield* 表达式
`yield*` 语句用来在一个 `Generator` 函数中执行另一个 `Generator` 函数。
### 作为对象属性的 Generator 函数
### Generator 函数的 this
### 含义
#### Generator 与状态机
#### Generator 与协程
### 应用
#### 异步操作的同步化表达
#### 控制流管理
#### 部署Iterator接口
#### 作为数据结构