生成器是特殊的函數。生成器函數在執行時能暫停,後面又能從暫停處執行。 show me the codingnode
function* zcGenerator() { // 這就是生成器
yield 'ak-47'
}
複製代碼
const zcInterator = zcGenerator()
複製代碼
調用生成器並不會執行函數,而是會返回一個迭代器,迭代器用來控制生成器的執行。調用迭代器的next()
方法,會向生成器請求一個值,生成器內的語句執行到第一個yield
的位置,並返回yield
後跟的值,此時生成器異步掛起執行,直到下次再請求值,生成器恢復執行,如此循環下去,直到最後生成器執行完畢。 next()
方法返回一個對象{value: '', done: ''}
, value
表示本次yield
返回的值,done
表示生成器後續時候仍是有yield語句,即生成器是否執行完畢。
從執行上下文的角度來理解迭代器,建立迭代器的時候,生成器入棧,初始化參數(若是有的話),執行完畢後出棧,而且銷燬當前執行環境,可是迭代器仍然保存着對他的引用,因此該環境並不會銷燬,這個東西和閉包很像。換個角度來講,生成器須要恢復執行,因此環境不能銷燬,保存在迭代器上。bash
function* weaponGenerator() {
yield 'ak47'
yield 'm16'
}
const weaponIterator = weaponGenerator()
console.log(weaponIterator.next()) // {value: 'ak47', done: false}
console.log(weaponIterator.next()) // {value: 'm16', done: false}
console.log(weaponIterator.next()) // {value: undefined, done: true}
複製代碼
使用yield*
表示將執行權交到另外一個生成器函數(當前函數暫停執行)閉包
function* weaponGeneratorInner() {
yield 'g56'
yield 'h58'
}
function* weaponGenerator() {
yield 'ak47'
yield* weaponGeneratorInner()
yield 'm16'
}
const weaponIterator = weaponGenerator()
console.log(weaponIterator.next()) // {value: 'ak47', done: false}
console.log(weaponIterator.next()) // {value: 'g56', done: false}
console.log(weaponIterator.next()) // {value: 'h58', done: false}
console.log(weaponIterator.next()) // {value: 'm16', done: false}
console.log(weaponIterator.next()) // {value: undefined, done: true}
複製代碼
交互方式有三種dom
function* parGenerator(weapon) {
yield weapon
}
const parIterator = parGenerator('j-20')
console.log(parIterator.next()) // { value: 'j-20', done: false }
複製代碼
next()
傳遞參數,與生成器交互 next()
的參數會傳遞給當前生成器正在yield
的值,所以,第一次調用next()
,生成器並無處於掛起的值,因此傳遞參數沒有任何意義,調用完第一個next()
方法,生成器開始執行,碰見第一個yield
,函數掛起,而且返回當前值;第二次調用next('j-30')
,j-30
會替換掉當前yield
的值j-20
,並繼續往下執行,將newWeapon
賦值爲j-30
,生成器執行完畢。function* parGenerator(weapon) {
const newWeapon = yield weapon
return newWeapon
}
const parIterator = parGenerator('j-20')
console.log(parIterator.next()) // { value: 'j-20', done: false }
console.log(parIterator.next('j-30')) // { value: 'j-30', done: true }
複製代碼
注意:由於經過
next()
傳遞參數只能傳遞給當前生成器yield
的值,所以第一次調用next()
傳遞參數沒有意義,因此,若是想爲生成器提供一個初始值,能夠向生成器函數穿第一個參數做爲初始值。異步
throw
方法與生成器交互function* throwGenerator() {
try {
yield 'good'
} catch (err) {
console.log(err)
}
}
const throwInterator = throwGenerator()
console.log(throwInterator.next())
console.log(throwInterator.throw('bad'))
複製代碼
throw
與next
傳參相似,只能對當前生成器yield
的值throw
,所以,在建立迭代器以後當即調用throw
沒有意義(會報錯),換個角度來講,只有代碼執行的時候才能夠try-catch
,在throw
以前沒有執行生成器,try-catch
也就沒什麼用。函數
注意:
throw
必須在next
以後調用,由於在調用第一個next
以後,生成器在第一個yield
處掛起,只有在請求下一個值或者throw
的時候纔會繼續日後執行try...catch...
,在throw的時候,生成器從新入棧,從yield 'good'
的地方繼續日後執行,生成器會將yield 'good'
做爲一個錯誤拋出,被catch
抓住。ui
function* idGenerator() {
let id = 0
while (true) {
yield ++id
}
}
const idIterator = idGenerator()
const id1 = idIterator.next().value
console.log(id1)
const id2 = idIterator.next().value
console.log(id2)
複製代碼
由於yield會暫時掛起函數執行,因此while
不會無限循環 2. 遍歷dom節點spa
function* elementGenerator(element) {
yield element
element = element.firstElementChild
while (element) {
elementGenerator(element)
element = element.nextElementSibling
}
}
for (let elementGeneratorElement of elementGenerator()) {
console.log(elementGeneratorElement.nodeName)
}
複製代碼