什麼是generator函式?
常規函式只會返回一個單一值(或者不返回任何值)。 而 Generator 可以按需一個接一個地返回(“yield”)多個值。它們可與 iterable 完美配合使用,從 而可以輕鬆地建立資料流。
如何建立一個generator函式,程式碼如下:
function* generator() { yield 1; yield '宋遠溪'; yield {name:'宋遠溪'} }
generator函式如何使用?繼續往下:
function* generator() { yield 1; yield '宋遠溪'; yield {name:'宋遠溪'} } const genFn = generator(); console.log( genFn.next(), genFn.next(), genFn.next() ) //{ value: 1, done: false } { value: '宋遠溪', done: false } { value: { name: '宋遠溪' }, done: false }
由程式碼可以觀察到,與普通函式不同的是generator函式呼叫會返回一個生成器物件,通過呼叫生成器物件的next(),可以返回generator函式體中的yield的值;
並且,通過呼叫next()我們可以觀察到,生成器物件時可迭代的!
yield 與 next 如何互動?繼續往下:
function* generator() { var name = yield "你叫什麼名字?"; yield `你好${name}`; } var iterable = generator(); console.log( iterable.next().value, iterable.next('李煥英').value ) //你叫什麼名字?你好李煥英
- 第一個 .next() 啟動了 generator 的執行……執行到達第一個 yield 。
- 結果被返回到外部程式碼中。
- 第二個 .next(4) 將 4 作為第一個 yield 的結果傳遞迴 generator 並恢復 generator 的執 行。
- ...以此類推
巢狀的generator,如何交換控制權,來看《JavaScript忍者祕籍第二版》的很經典的一個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Generator函式</title> </head> <body> <div id="box"> <form action=""> <label for=""> <input type="text"> </label> <button>提交</button> </form> <p>Generator</p> </div> <script> var box = box || document.getElementById('box'); function* traverseTree(element) { yield element; element = element.firstElementChild; while (element) { yield* traverseTree(element); element = element.nextElementSibling; } } const subTree = traverseTree(box); for (const element of subTree) { console.log(element.nodeName); /** * DIV * FORM * LABEL * INPUT * BUTTON * P */ } </script> </body> </html>
我們可以通過yield* 將當前generator函式的控制權轉移到下一個generator函式中,讓我們再來看一下不用生成器函式的遍歷方法,程式碼如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Generator函式</title> </head> <body> <div id="box"> <form action=""> <label for=""> <input type="text" /> </label> <button>提交</button> </form> <p>Generator</p> </div> <script> var box = box || document.getElementById("box"); function domTraverse(element, callback) { callback(element); element = element.firstElementChild; while (element) { domTraverse(element, callback); element = element.nextElementSibling; } } domTraverse(box, (element) => { console.log(element.nodeName); /** * DIV * FORM * LABEL * INPUT * BUTTON * P */ }); </script> </body> </html>
通過這兩段程式碼可以看出,我們通過生成器函式將生產值(HTML節點) 與消費值(for 迴圈列印 訪問過的節點名稱)的程式碼分隔開,使得程式碼解耦;
我們還可以通過generator丟擲錯誤,程式碼如下:
function* generator() { try { var result = yield '你喜歡JavaScript嗎'; if (result == '喜歡') yield '太棒了!' else yield '真遺憾' }catch(e) { console.log('出錯了!'); console.log(e); } } const gen = generator(); console.log( gen.next(), ) gen.throw(new Error('故意丟擲錯誤!')); //出錯了,Error:......