一個很簡短的 JS 生成器入門和用法參考

生成器函數

在寫其餘 js 代碼時會常常用到 debugger 的東西,可以讓當前運行的函數暫停住。生成器函數裏的 yield 關鍵詞也能使得函數暫停運行,同時還能用來作數據的輸入輸出(若是是 debugger,只能手動去逐個 inspect 變量)。html

yield 能夠看成「斷點」。yield value 除了能看成斷點,還能夠向外部輸出 valuereact

此外,yield 表達式自己也是能夠有值的,也就是說還能夠表示外部傳入的數據。只要外部在調用 next(inputValue) 時輸入一個值。 這就是爲何在賦值語句裏,能夠用 yield 做爲右值(例如,在使用了 react-saga 的項目裏處處都是這種玩意兒)。git

function* fn1(){
  var data = yield "字符串反轉器已啓動"
  yield data.split("").reverse().join("")
}

// 建立 Generator 對象
var g = fn1()

// 運行函數,直到遇到第一個「斷點」(yield 處)
console.log(g.next())

// 上次停頓的位置是 var data = yield "字符串反轉器已啓動"
// 所以能夠在繼續運行前,使得 (yield "字符串反轉器已啓動") 的值爲 "Hello"
console.log(g.next("Hello")) 

// 輸出:
// { value: '字符串反轉器已啓動', done: false }
// { value: 'olleH', done: false }
複製代碼

Generator

正如前面的示例,調用一個 function* 生成器函數,會返回一個 Generator 對象(上文的 g)。這個對象是可迭代對象(最多見的用法,即用於 for of 循環中)。github

此外,能夠把它看成一個控制器,控制着一個被 yield 打了斷點(並且還沒開始運行)的函數。數組

Generator 其具有下列方法:bash

  1. next(inputValue?): { value, done }
    • (從頭,或者從上次中止的位置)開始運行生成器函數體,直到遇到 yield 或者 return
    • 注意:此函數能夠有一個可選的參數 inputValue 。能夠在繼續運行生成器函數前,爲上次停頓所在的 yield 表達式設置一個值。
  2. throw(e): { value, done }
    • 若是生成器函數沒開始運行,則等同於原地 throw
    • 不然,在生成器函數體當前 yield 停頓的位置 throw 一個異常,而後繼續運行,直到遇到 yieldreturn 或者未被捕獲的異常(見下文)
  3. return(value): {value, done}
    • 強行終止(即便生成器函數還沒運行完畢),並指定一個 value 做爲返回值
  4. [Symbol.iterator]()
    • 用於迭代協議的。效果同直接調用 next()

yield*

yield 關鍵詞相比,多了一個星號。能夠把 yield* another_iterable 看成如下代碼語法糖:編輯器

for (let item of another_iterable) {
  yield item;
}
複製代碼

也就是說,在生成器函數裏yield* 就是針對一個可迭代對象,把它的每一項逐個地 yield 出來。函數

有趣的例子:ui

  • yield* [1,2,3] 會把3個數字逐個 yield 出來。數組是可迭代的。
  • yield* "abcdefg" 會把這7個字母逐個 yield 出來。字符串也是可迭代對象。
  • yield* another_generator 至關於把另外一個 Generator 的輸出,看成本身的輸出給一個個 yield 出去了。
    • 這個有趣的特性能夠用來作攔截器之類的應用。
    • 說到了串聯,若是是當前生成器函數想要利用另一個生成器的返回值,直接調用 another_generator.next().value 就好了

生成器函數內的異常

next() 或者 throw(e) 可讓生成器函數開始運行。在運行過程當中,若是生成器函數裏遇到了未被捕獲的異常(能夠是生成器內部本身產生的,或者由外部調用 throw(e) 塞進去的),那麼會在外部由 next() 或者 throw(e) 給 throw 出來spa

function* test(){
  try {
    console.log("inner: Hello")
    console.log("inner: GET" + (yield "output1"))
  } catch (err) {
    console.log("inner: Caught", err)
  }
  console.log("inner: Done")
  return "output 2"
}

var g = test()
console.log("outer: next: ", g.next("input 1"))
console.log("outer: throw: ", g.throw("err"))
console.log("outer: next: ", g.next("input 2"))
複製代碼

上面的例子輸出以下。注意到由外部提供的 input1 不會被輸出,由於那是生成器函數還沒開始運行的時候傳進去的,能傳到哪裏?沒有任何的意義。

inner: Hello
outer: next:  { value: 'output1', done: false }
inner: Caught err
inner: Done
outer: throw:  { value: 'output 2', done: true }
outer: next:  { value: undefined, done: true }
複製代碼

安利

寫了一個 Markdown 編輯器組件,只要一個框,所見即所得,並且還全面兼容 CodeMirror(一個很強大的代碼編輯組件)

悄悄安利一下: laobubu.net/HyperMD/

(歡迎來 GitHub 點贊 或者幫我買杯咖啡

相關文章
相關標籤/搜索