## 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接口
#### 作為數據結構