Generator(生成器)
1.引入案例:讓遍歷停下來
ES5
function loop () {
for (let i = 0; i < 5; i++) {
console.log(i)
}
}
loop()
ES6
function * loop () {
for (let i = 0; i < 5; i++) {
yield console.log(i)
}
}
const l = loop()
l.next()
l.next()
l.next()
2.Next
- Generator函數函數名之前需要加
*
- 在需要控制的語句前加 yield
- 可嵌套使用
- Generator對象調用next函數來執行被控制的語句,執行時返回yield後面表達式的返回值和Generator對象執行狀態
function * f () {
yield console.log(1)
yield console.log(2)
let val
console.log(val = yield [1, 2, 3]) // val,將整個yield表達式的值替換爲next的參數
console.log(yield val) // 10,整個yield表達式的值,即傳給next的參數
}
const l = f()
console.log(l.next(10)) // {value: undefined, done: false} 第一個yield後面表達式的返回值
console.log(l.next(10)) // {value: undefined, done: false} 第二個yield後面表達式的返回值
console.log(l.next(10)) // {value: Array(3), done: false} 第三個yield後面表達式的返回值
console.log(l.next({ 'A': 'a', 'B': 'b' })) // {value: {A: "a", B: "b"}, done: false} 將參數傳遞給整個yield表達式並完成
console.log(l.next(10)) // {value: undefined, done: true}
/**
* 只有當整個Generator函數執行完畢(最後一個yield執行完畢),狀態done纔會置爲true(其實返回的是上一個next執行完後的狀態)
*/
3.Return
function * f () {
yield console.log(1) // 1
yield console.log(2)
let val
console.log(val = yield [1, 2, 3])
console.log(yield val)
}
const l = f()
console.log(l.next('next')) // {value: undefined, done: false}
console.log(l.return('return')) // {value: "return", done: true}, 類似return關鍵字,參數直接影響最終return函數的返回值
4.Throw
function * f () {
try {
yield console.log(1) // 1
yield console.log(2)
yield console.log(3)
} catch (e) {
console.log(e.message) // throw
} finally {
let val
console.log(val = yield [1, 2, 3]) // yield表達式的值——next的參數‘next’
console.log(yield val) // yield表達式的值——next的參數‘next’
}
}
const l = f()
console.log(l.next('next')) // {value: undefined, done: false}
console.log(l.throw(new Error('throw'))) // {value: Array(3), done: false},如果所執行代碼外沒有try catch就拋出異常,報錯,直接中斷
console.log(l.next('next')) // {value: "next", done: false},返回值爲yield表達式的值——next的參數‘next’
console.log(l.next('next')) // {value: undefined, done: true}yield表達式的值——undefined
5.案例:抽獎
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']
let thirdPrize = ['3A', '3B', '3C', '3D', '3E', '3F', '3G', '3H', '3I', '3J', '3K', '3L', '3M', '3N', '3O', '3P']
let result = []
let random
// 抽一等獎
for (let i = 0; i < first; i++) {
random = Math.floor(Math.random() * firstPrize.length)
// 使用concat合併數組,使用splice刪除數組中選中的元素並返回被刪除的元素合併到result數組中
result = result.concat(firstPrize.splice(random, 1))
}
// 抽二等獎
for (let i = 0; i < second; i++) {
random = Math.floor(Math.random() * secondPrize.length)
// 使用concat合併數組,使用splice刪除數組中選中的元素並返回被刪除的元素合併到result數組中
result = result.concat(secondPrize.splice(random, 1))
}
// 抽三等獎
for (let i = 0; i < third; i++) {
random = Math.floor(Math.random() * thirdPrize.length)
// 使用concat合併數組,使用splice刪除數組中選中的元素並返回被刪除的元素合併到result數組中
result = result.concat(thirdPrize.splice(random, 1))
}
return result
}
let t = draw()
for (let value of t) {
console.log(value)
}
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']
let thirdPrize = ['3A', '3B', '3C', '3D', '3E', '3F', '3G', '3H', '3I', '3J', '3K', '3L', '3M', '3N', '3O', '3P']
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 {
console.log('本輪抽獎結束')
return false
}
}
}
let t = draw()
console.log('下面輸出一等獎名單:')
console.log(t.next().value)
console.log('下面輸出二等獎名單:')
console.log(t.next().value)
console.log(t.next().value)
console.log(t.next().value)
console.log('下面輸出三等獎名單:')
console.log(t.next().value)
console.log(t.next().value)
console.log(t.next().value)
console.log(t.next().value)
console.log(t.next().value)
console.log(t.next().value)
6.案例:用Generator實現一個斐波那契數列
function * f () {
let sum1 = 0
let sum2 = 1
yield sum1
yield sum2
while (1) {
yield sum1 = sum1 + sum2;
[sum1, sum2] = [sum2, sum1] // sum1 = [sum2, sum2 = sum1][0] // 交換
}
}
let l = f()
for (let i = 0; i < 12; i++) {
console.log(l.next().value)
}
拓展: