關注前端小謳,閱讀更多原創技術文章
相關代碼 →javascript
*
function* generatorFn() {} // 生成器函數聲明 let gfn = function* () {} // 生成器函數表達式 let foo = { *generatorFn() {}, // 生成器函數做爲對象字面量方法 } class Foo { *generatorFn() {} // 生成器函數做爲類實例方法 } class FooStatic { static *generatorFn() {} // 生成器函數做爲類靜態方法 }
next()
方法const g = generatorFn() // 調用生成器函數,產生生成器對象 console.log(g) // generatorFn {<suspended>},生成器對象 console.log(g.next) // 生成器對象具備next()方法
next()
方法的返回值相似於迭代器,有done 屬性和value 屬性next()
就會達到done:true
狀態console.log(g.next()) // { value: undefined, done: true },函數體爲空
value
的返回值(默認爲 undefined)function* generatorFn2() { return 'foo' } const g2 = generatorFn2() console.log(g2.next()) // { value: 'foo', done: true } console.log(g2.next()) // { value: undefined, done: true },耗盡
next()
方法後開始執行function* generatorFn3() { console.log('生成器函數開始執行') } const g3 = generatorFn3() // 調用生成器函數,產生生成器對象(生成器函數還未執行,不打印日誌) g3.next() // '生成器函數開始執行',初次調用next()方法,生成器函數開始執行,打印日誌
function* generatorFn4() {} console.log(generatorFn4) // ƒ* generatorFn4() {},生成器函數 const g4 = generatorFn4() console.log(g4) // generatorFn4 {<suspended>},生成器對象 console.log(g4[Symbol.iterator]) // ƒ [Symbol.iterator]() { [native code] },迭代器工廠函數 console.log(g4[Symbol.iterator]()) // generatorFn4 {<suspended>},迭代器 console.log(g4 === g4[Symbol.iterator]()) // true,生成器對象的默認迭代器是自引用的
yield
關鍵字可讓生成器中止和開始執行:前端
yield
關鍵字以前正常執行yield
關鍵字後中止執行,函數做用域的狀態被保留next()
方法恢復執行function* generatorFn5() { yield } let g5 = generatorFn5() console.log(g5.next()) // { value: undefined, done: false },yield生成的值 console.log(g5.next()) // { value: undefined, done: true },恢復執行生成的值
yield
關鍵字與函數的return
語句做用類似,其生成的值會出如今next()
方法返回的對象裏,但done
的狀態不一樣:java
yield
關鍵字退出的生成器函數會處在done:false
狀態return
關鍵字退出的生成器函數會處在done:true
狀態function* generatorFn6() { yield 'foo' yield 'bar' return 'baz' } let g6 = generatorFn6() console.log(g6.next()) // { value: 'foo', done: false },yield關鍵字退出 console.log(g6.next()) // { value: 'bar', done: false },yield關鍵字退出 console.log(g6.next()) // { value: 'baz', done: true },return關鍵字退出
next()
方法不影響其餘生成器let g7 = generatorFn6() // 生成器對象g7 let g8 = generatorFn6() // 生成器對象g8 console.log(g7.next()) // { value: 'foo', done: false } console.log(g8.next()) // { value: 'foo', done: false } console.log(g8.next()) // { value: 'bar', done: false } console.log(g7.next()) // { value: 'bar', done: false }
yield
關鍵字必須在生成器函數內部,直接位於生成器函數定義中使用,用在其餘地方或嵌套的非生成器函數會報錯function* validGeneratorFn() { yield 'result' } function* invalidGeneratorFnA() { function a() { yield 'result' // SyntaxError: Unexpected string } } function* invalidGeneratorFnB() { const b = () => { yield 'result' // SyntaxError: Unexpected string } } function* invalidGeneratorFnC() { ;(() => { yield 'result' // SyntaxError: Unexpected string })() }
function* generatorFn7() { // 生成器函數 yield 1 yield 2 yield 3 } for (const x of generatorFn7()) { // 調用生成器函數generatorFn7,generatorFn7()是生成器對象 console.log(x) /* 1 2 3 */ }
function* nTimes(n) { while (n--) { console.log(n) yield } } for (let _ of nTimes(3)) { console.log(_) /* 2,第1次循環n undefined,第1次循環yield 1,第2次循環n undefined,第2次循環yield 0,第3次循環n undefined,第3次循環yield */ }
除了做爲函數的中間返回語句使用,yield
關鍵字還能夠做爲函數的中間參數使用git
yield
關鍵字會接收到傳給next()
方法的第一個值next()
傳入的值不會被使用,由於僅僅是爲了開始執行生成器函數function* generatorFn8() { console.log(yield) console.log(yield) console.log(yield) } let g9 = generatorFn8() // 調用生成器函數,產生生成器對象 g9.next('bar') // 第一次調用next()的值不會被使用,僅做爲開始執行生成器函數 g9.next('baz') // 'baz',調用next()傳入baz,參數做爲交給同一個yield的值 g9.next('qux') // 'qux',調用next()傳入qux,參數做爲交給同一個yield的值
yield
關鍵字同時用於輸入和輸出(與return
關鍵字同時使用)github
next()
方法沒有參數,生成器函數直到遇到yield
關鍵字中止執行next()
方法有參數,參數做爲交給同一個 yield 的值,生成器函數執行return
返回本次傳入的值function* generatorFn9() { return yield 'foo' } let g10 = generatorFn9() console.log(g10.next()) // { value: 'foo', done: false },next()沒有參數,遇到yield關鍵字暫停執行,並計算要產生的值 console.log(g10.next('bar')) // { value: 'bar', done: true },next()有參數,參數做爲交給同一個yield的值,至關於return 'bar'
yield
關鍵字屢次使用function* generatorFn10() { for (let i = 0; ; i++) { yield i } } let g11 = generatorFn10() console.log(g11.next()) // { value: 0, done: false } console.log(g11.next()) // { value: 1, done: false } console.log(g11.next()) // { value: 2, done: false } console.log(g11.next()) // { value: 3, done: false }
function* nTimes(n) { let i = 0 while (n--) { yield i++ } } for (let x of nTimes(3)) { console.log(x) /* 0 1 2 */ }
function* range(start, end) { while (end > start) { yield start++ } } for (const x of range(4, 7)) { console.log(x) /* 4 5 6 */ }
function* zeros(n) { while (n--) { yield 0 } } console.log(zeros(8)) // zeros {<suspended>},生成器對象 console.log(Array.from(zeros(8))) // [0, 0, 0, 0, 0, 0, 0, 0],生成器對象做爲可迭代對象
function* fibonacci() { let arr = [0, 1] let [prev, curr] = arr while (true) { ;[prev, curr] = [curr, prev + curr] arr.push(curr) yield arr } } function Fibonacci(n) { if (n === 1) { // 第1項 return 0 } else if (n === 2 || n === 3) { // 第二、3項 return 1 } else { // 第4項或以後 let num = 0 const fibo = fibonacci() for (let i = 3; i <= n; i++) { num = fibo.next().value } return num } } console.log(Fibonacci(8).join()) // 0,1,1,2,3,5,8,13
*
加強yield
,讓其可以迭代一個可迭代對象,yield*
將一個可迭代對象序列化爲一連串單獨產出的值function* generatorFn11() { yield* [1, 2, 3] } let g12 = generatorFn11() for (const x of generatorFn11()) { console.log(x) /* 1 2 3 */ } // 等價於 function* generatorFn11() { for (const x of [1, 2, 3]) { yield x } }
yield*
的值是關聯迭代器返回done:true
時value
的屬性:算法
done:true
表明迭代器耗盡,這個值是 undefined
function* generatorFn12() { console.log('iterValue', yield* [1, 2, 3]) } for (const x of generatorFn12()) { console.log('value', x) /* value 1 value 2 value 3 iterValue undefined */ }
done:true
的值是return 返回的值(沒有 return 值則返回 undefined)function* innerGeneratorFn() { yield 'foo' return 'bar' } function* outerGeneratorFn() { console.log('iterValue', yield* innerGeneratorFn()) } for (const x of outerGeneratorFn()) { console.log('value', x) /* value foo iterValue bar */ }
yield*
實現遞歸算法yield*
實現遞歸,此時生成器能夠產生自身function* nTimes(n) { if (n > 0) { yield* nTimes(n - 1) // 生成器對象做爲可迭代對象 yield n } } for (const x of nTimes(3)) { console.log(x) /* 1 2 3 */ }
Iterable
接口,生成器函數和默認迭代器被調用以後都產生迭代器class Foo2 { // Foo既是迭代器,又是生成器函數 constructor() { this.values = [1, 2, 3] } *[Symbol.iterator]() { yield* this.values } } const f = new Foo2() // 產生可迭代的生成器對象 for (const x of f) { console.log(x) /* 1 2 3 */ }
Iterator
接口的對象必定有next()
方法,還有一個可選的return()
方法,生成器還有第三個方法throw()
return()
和throw()
均可以用於強制生成器進入關閉狀態function* generatorFn13() {} let g13 = generatorFn13() // 生成器對象 console.log(g13.next) // ƒ next() { [native code] } console.log(g13.return) // ƒ return() { [native code] } console.log(g13.throw) // ƒ throw() { [native code] }
return()
方法返回種種迭代器對象的值(即 return()方法的參數)數組
function* generatorFn14() { yield* [1, 2, 3] } let g14 = generatorFn14() console.log(g14) // generatorFn14 {<suspended>} console.log(g14.return(5)) // {value: 5, done: true} console.log(g14) // generatorFn14 {<closed>}
return()
方法進入關閉狀態的生成器對象,後續調用next()
都會顯示done:true
狀態,後續提供的任何返回值都再也不被存儲或傳播console.log(g14.next()) // { value: undefined, done: true },已經調用過return() console.log(g14.next()) // { value: undefined, done: true } console.log(g14.next()) // { value: undefined, done: true }
for-of
等內置語言結構會忽略狀態爲done:true
的迭代器對象內部返回的值(忽略 undefined)let g15 = generatorFn14() for (const x of g15) { x > 1 && g15.return() // x大於1則中止生成器 console.log(x) /* 1 2 自動忽略done:true返回的value(undefined) */ }
throw()
方法會在暫停的時候,將一個提供的錯誤注入到生成器對象中函數
function* generatorFn15() { yield* [1, 2, 3] } let g16 = generatorFn15() console.log(g16) // generatorFn15 {<suspended>} try { g16.throw('foo') // 注入錯誤 } catch (err) { console.log(err) // 'foo' } console.log(g16) // generatorFn15 {<closed>},錯誤未被處理,生成器關閉
yield
function* generatorFn16() { for (const x of [1, 2, 3]) { // 錯誤在生成器的try/catch塊中拋出 -> (生成器對象已開始執行)在生成器內部被捕獲 // 若生成器對象未開始執行,則throw()拋出的錯誤不會在生成器內部被捕獲 try { yield x // 在yield關鍵字處暫停執行 } catch (err) { console.log(err) // 'foo' } } } let g17 = generatorFn16() console.log(g17.next()) // { value: 1, done: false } g17.throw('foo') // 注入錯誤 console.log(g17.next()) // { value: 3, done: false },跳過對應的yield
請使用生成器函數和 yield 關鍵字,分別用代碼實現如下功能:this
yield*
的做用是什麼?在普通迭代器、生成器函數產生的迭代器中,yield*
的值分別是什麼?