ES6(十四)—— Generator

目錄

  • ES6如何讓遍歷「停」下來
  • Basic Syntax —— 基礎語法
  • Senior Syntax —— 高級語法編程

    • next添加參數
    • return控制結束
    • throw拋出異常控制
  • Scene Pratice —— 案例dom

    • 抽獎
    • 數3的倍數小遊戲
    • 使用Generator函數實現Iterator方法
  • ES6-ES10學習版圖

ES6如何讓遍歷「停」下來

ES5循環一旦執行,沒法停下來的異步

function loop() {
  for(let i = 0; i < 5; i++) {
    console.log(i)
  }
}

loop()
// 0
// 1
// 2
// 3
// 5

使用Generator,怎麼改造?異步編程

// 修改一,在loop前面加一個星號
function * loop() {
  for(let i = 0; i < 5; i++) {
  // 修改二:在輸出前面加yield
    yield console.log(i)
  }
}
// 修改三:定義一個變量將loop賦值給l
const l = loop()

// 這個時候並無輸出,若要輸出調用next方法
l.next() // 0
l.next() // 1
l.next() // 2
l.next() // 3
l.next() // 4
l.next() // 以後不會輸出任何東西

//應用場景:年會抽獎、自定義遍歷器

Basic Syntax —— 基礎語法

  1. 遍歷器就是一個函數,可是與普通的函數不一樣。
  2. 函數內部能夠使用yield停下來
  3. 使用實例調用next控制循環
  4. Generator函數的定義不能使用箭頭函數,不然會出發報錯SyntaxError
function * gen() {
  let val
  val = yield 1
  console.log(val)
}

const l = gen()
// "Generator { }"

l.next() // 沒有任何輸出
l.next() // undefined  yield表達式沒有返回值,因此返回undefined

next()的返回值函數

第一個參數是返回的值,oop

第二個參數是是否遍歷完成,false是沒有遍歷完,true是遍歷完成學習

function * gen() {
  let val
  val = yield [1, 2, 3]
  console.log(val) // undefined
}

const l = gen()

console.log(l.next()) // {value: Array(3), done: false}
console.log(l.next()) // {value: undefined, done: true}
function * gen() {
  let val
  // yield 後面加了一個星號,後面是一個遍歷的對象,因此能夠嵌套一個Generator對象
  val = yield * [1, 2, 3]
  console.log(val) // undefined
}

const l = gen()

console.log(l.next()) // {value: 1, done: false}
console.log(l.next()) // {value: 2, done: false}

學到這裏要明白:this

  1. Generator是作什麼用的?<br/>

控制循環流程用的<br/>
最重要的做用是解決異步編程嵌套層級較深的問題。spa

  1. yield有沒有返回值?<br/>

沒有,可是遍歷器對象的next方法能夠修改這個默認值code

  1. ES5相比,是如何控制程序的中止和啓動的?<br/>

使用yield去控制中止,使用next去控制啓動

Senior Syntax —— 高級語法

如何在函數外部控制函數內部的運行?

next添加參數

next函數寫參數,做爲yield的返回值

function * gen() {
  let val
  val = yield [1, 2, 3]
  console.log(val) // 20
}

const l = gen()

console.log(l.next(10))// {value: Array(3), done: false}
// 此時yield沒有賦值,因此10並無用
console.log(l.next(20))// {value: undefined, done: true}
// 此時yield對val進行賦值操做,yield表達式的值是20

講義的例子能夠理解更深入

function * gen() {
  var val = 100
  while(true){
    console.log(`before${val}`)
    val = yield val
    console.log(`return ${val}`)
  }
}

let g = gen()
console.log(g.next(20).value)
// before 100
// 100
console.log(g.next(30).value)
// return 30
// before 30
// 30
console.log(g.next(40).value)
// return 40
// before 40
// 40
1. g.next(20) 這句代碼會執行 gen 內部的代碼,遇到第一個 yield 暫停。因此 console.log("before "+val) 執行輸出了 before 100,此時的 val100,因此執行到 yield val 返回了 100,注意 yield val 並無賦值給 val

2.g.next(30) 這句代碼會繼續執行 gen 內部的代碼,也就是 val = yield val 這句,由於 next 傳入了 30,因此 yield val 這個返回值就是 30,所以 val 被賦值 30,執行到console.log("return "+val)輸出了 30,此時沒有遇到 yield 代碼繼續執行,也就是 while 的判斷,繼續執行console.log("before "+val) 輸出了 before 30,再執行遇到了yield val程序暫停。

3.g.next(40) 重複步驟 2

return控制結束

function * gen() {
  let val
  val = yield [1, 2, 3]
  console.log(val) // 沒有執行
}

const l = gen()

console.log(l.next(10))// {value: Array(3), done: false}
console.log(l.return())// {value: undefined, done: true}
//返回操做,函數終止
console.log(l.next(20))// {value: undefined, done: true}

添加返回值的參數

function * gen() {
  let val
  val = yield [1, 2, 3]
  console.log(val) // 沒有執行
}

const l = gen()

console.log(l.next(10))// {value: Array(3), done: false}
console.log(l.return(100))// {value: 100, done: true}
//返回操做,函數終止
console.log(l.next(20))// {value: undefined, done: true}

throw拋出異常控制

function * gen() {
  while (true) {
    try {
      yield 1
    } catch (e) {
      console.log(e.message) // ss
    }
  }
}

const l = gen()

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

l.throw(new Error('ss')) 
// 拋出錯誤,執行catch
console.log(l.next()) //{value: 1, done: false}

Scene Pratice —— 案例

抽獎

ES5

function draw (first = 1, second = 3, third = 5) {
  // 三個獎的候選人,一個結果,一個隨機數
  let firstPrize = ['1A', '1B', '1C', '1D', '1E']
  let secondPrize = ['2A', '2B', '2C', '2D', '2E', '2F', '2G', '2H', '2I', '2J', '2K', '2L']
  let thirdPrize = ['3A', '3B', '3C', '3D', '3E', '3F', '3G', '3H', '3I', '3J', '3K', '3L', '3M', '3N', '3O', '3P', '3Q', '3R', '3S', '3T', '3U', '3V', '3W', '3X', '3Y', '3Z']
  let result = []
  let random
  // 抽一等獎
  for(let i = 0; i < first; i++){
    random = Math.floor(Math.random() * firstPrize.length)
    result = result.concat(firstPrize.splice(random, 1))
  }
  // 抽二等獎
  for(let i = 0; i < second; i++){
    random = Math.floor(Math.random() * secondPrize.length)
    result = result.concat(secondPrize.splice(random, 1))
  }
  // 抽三等獎
  for(let i = 0; i < third; i++){
    random = Math.floor(Math.random() * thirdPrize.length)
    result = result.concat(thirdPrize.splice(random, 1))
  }
  return result
}

console.log(draw())
// ["1A", "2D", "2K", "2A", "3A", "3G", "3Y", "3W", "3P"]

ES6

function * draw (first = 1, second = 3, third = 5) {
  // 三個獎的候選人,一個結果,一個隨機數
  let firstPrize = ['1A', '1B', '1C', '1D', '1E']
  let secondPrize = ['2A', '2B', '2C', '2D', '2E', '2F', '2G', '2H', '2I', '2J', '2K', '2L']
  let thirdPrize = ['3A', '3B', '3C', '3D', '3E', '3F', '3G', '3H', '3I', '3J', '3K', '3L', '3M', '3N', '3O', '3P', '3Q', '3R', '3S', '3T', '3U', '3V', '3W', '3X', '3Y', '3Z']
  let count = 0
  let random
  while(1){
    if (count < first) {
      random = Math.floor(Math.random() * firstPrize.length)
      yield firstPrize[random]
      count ++
      firstPrize.splice(random, 1)
    } else if (count < first + second) {
      random = Math.floor(Math.random() * secondPrize.length)
      yield secondPrize[random]
      count ++
      secondPrize.splice(random, 1)
    } else if (count < first + second + third) {
      random = Math.floor(Math.random() * thirdPrize.length)
      yield thirdPrize[random]
      count ++
      thirdPrize.splice(random, 1)
    } else {
      return false
    }
  }
  
}

let d = draw()
console.log(d.next().value) // 1C
console.log(d.next().value) // 2E
console.log(d.next().value) // 2H
console.log(d.next().value) // 2C
console.log(d.next().value) // 3H
console.log(d.next().value) // 3V
console.log(d.next().value) // 3A
console.log(d.next().value) // 3J
console.log(d.next().value) // 3N
console.log(d.next().value) // false
console.log(d.next().value) // undefined
console.log(d.next().value) // undefined

數3的倍數小遊戲

若是是ES5,是無限死循環,程序崩潰

ES6

function * count (x = 1) {
  while (1) {
    if (x % 3 === 0) {
      yield x
    }
    x++
  }
}

let num = count()
console.log(num.next().value) // 3
console.log(num.next().value) // 6
console.log(num.next().value) // 9
console.log(num.next().value) // 12
console.log(num.next().value) // 15
console.log(num.next().value) // 18
...

使用Generator函數實現Iterator方法

const todos = {
  life: ['吃飯', '睡覺', '打豆豆'],
  learn: ['語文', '數學', '外語'],
  work: ['喝茶'],

  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for( const item of all) {
      callback(item)
    }
  },
  [Symbol.iterator]: function * () {
    const all = [...this.life, ...this.learn, ...this.work]
    for(const item of all) {
      yield item
    }
  }
}

for(const item of todos){
  console.log(item)
}
// 吃飯
// 睡覺
// 打豆豆
// 語文
// 數學
// 外語
// 喝茶

學習版圖