【ES6系列】Generator

通俗的讲 Generators 是可以用来控制迭代器的函数。

// ES5循环写法
function loop() {
  for (let i = 0; i < 5; i++) {
    console.log(i)
  }
}
loop()

// ES6 generator 写法
function * loop() {
  for (let i = 0; i < 5; i++) {
    yield console.log(i) // 函数内部使用yield暂停程序执行
  }
}
const l = loop()
l.next() // 0 函数外部使用next()恢复执行
l.next() // 1
l.next() // 2
l.next() // 3
l.next() // 4

常规的循环只能一次遍历完所有值,Generator 可以通过调用 next 方法拿到依次遍历的值,让遍历的执行变得“可控”。

语法

function * gen () {
  yield 1
  yield 2
  yield 3
}

let g = gen()
// "Generator { }"

以上是 Generator 的定义方法,有几个点值得注意:

  1. 比普通函数多一个 *
  2. 函数内部用 yield 来控制程序的执行的“暂停”
  3. 函数的返回值通过调用 next 来“恢复”程序执行
function* gen() {
  let val
  val = yield 1
  console.log(val)
}
const l = gen()
l.next() // 找到 yield 但没有代码需要执行
l.next() // 未找到yield,没有返回值 undefined
// Generator原理: Generator 函数返回一个对象,对象有next()方法,next()在函数体内找yield或函数结尾,找到一个就结束

Generator基本语法描述:

1、Generator 是一个函数,在定义时比普通函数多一个*

2、在Generator 函数内部,可以通过 yield 控制程序停止执行

3、Generator 函数可以通过在yield 后加*进行嵌套

4、可以用next()恢复执行,同时会返回当前执行的状态和数据{value: 当前的执行结果, done: 是否执行完毕}

 Generator 读法扩展:

function* gen() {
  let val
  val = (yield [1, 2, 3]) + 7
  console.log(val)
}
const l = gen()
// next()如果传参数则是作为 yield 表达式的返回值,替换yeld表达式,不传参数则yield 表达式返回 undefined
console.log(l.next(10)) 
console.log(l.return()) // return 方法可以让 Generator 遍历终止,有点类似 for 循环的 break。
console.log(l.next(20))

 Generator 高级语法:

    1、next()可以传值,用来修改内部运行数据。如果传参数则是作为 yield 表达式的返回值,替换yeld表达式,不传参数则yield 表达式返回 undefined

    2、return() 方法可以让 Generator 遍历提前终止,有点类似 for 循环的 break。

    3、可以从外部向内部抛出异常,在内部通过 try{}catch(e){} 捕获异常。程序运行不会受影响

// 通过return() 提前停止迭代
function* gen() {
  let val
  val = (yield [1, 2, 3]) + 7
  console.log(val)
}
const l = gen()
console.log(l.next(10)) // {value: Array(3), done: false}
console.log(l.return(100)) // {value: 100, done: true}
console.log(l.next(20)) // {value: undefined, done: true}

// 函数外部抛出异常,函数内部捕获异常
function* gen() {
  while (true) {
    try {
      yield 1
    } catch (e) {
      console.log(e.message)
    }
  }
}
const g = gen()
console.log(g.next()) // {value: 1, done: false}
console.log(g.next()) // {value: 1, done: false}
console.log(g.next()) // {value: 1, done: false}
console.log(g.next()) // {value: 1, done: false}
console.log(g.next()) // {value: 1, done: false}
g.throw(new Error('something error')) // something error
console.log(g.next()) // {value: 1, done: false}

 

业务场景:年会抽奖模拟 一等奖:一名 二等奖:三名 三等奖:五名

// 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']
  let thirdPrize = ['3A', '3B', , '3C', '3D', '3E', '3F', '3G', '3H', '3I', '3K', '3J']
  let result = []
  let random
  // 抽一等奖
  for (let i = 0; i < first; i++) {
    random = Math.floor(Math.random() * firstPrize.length)
    result = result.concat(firstPrize.splice(random, 1))
  }
  // 抽二等奖
  for (let i = 0; i < second; i++) {
    random = Math.floor(Math.random() * secondPrize.length)
    result = result.concat(secondPrize.splice(random, 1))
  }
  // 抽三等奖
  for (let i = 0; i < third; i++) {
    random = Math.floor(Math.random() * thirdPrize.length)
    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']
  let thirdPrize = ['3A', '3B', , '3C', '3D', '3E', '3F', '3G', '3H', '3I', '3K', '3J']
  let count = 0
  let random
  while (1) {
    if (count < first) { // 抽一等奖
      random = Math.floor(Math.random() * firstPrize.length)
      yield firstPrize[random]
      count++ // 抽一次计数器+1
      firstPrize.splice(random, 1)
    } else if (count < first + second) { // 抽二等奖
      random = Math.floor(Math.random() * secondPrize.length)
      yield secondPrize[random]
      count++ // 抽一次计数器+1
      secondPrize.splice(random, 1)
    } else if (count < first + second + third) {
      random = Math.floor(Math.random() * thirdPrize.length)
      yield thirdPrize[random]
      count++ // 抽一次计数器+1
      thirdPrize.splice(random, 1)
    } else { // 如果三个奖项都抽完
      return false
    }
  }
}
let d = draw()
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value)
console.log(d.next().value) // false

场景二:输出3的倍数。只要调用就执行并且有值

function* count(x = 1) {
  while (1) {
    if (x % 3 === 0) {
      yield x
    }
    x++
  }
}
let num = count()
console.log(num.next().value)
console.log(num.next().value)
console.log(num.next().value)
console.log(num.next().value)
console.log(num.next().value)
console.log(num.next().value)

思考:

1、用 Generator 实现一个斐波那契数列

             斐波那契数列 特性:从第三个数开始,值是前两个数之和

// 递归实现方法:
function fibonacci(m) {
  if (m < 3) return m
  else return fibonacci(m - 1) + fibonacci(m - 2)
// else return arguments.callee(m - 1) + arguments.callee(m - 2)
}
console.log(fibonacci(1)) // 1
console.log(fibonacci(2)) // 2
console.log(fibonacci(3)) // 3
console.log(fibonacci(4)) // 5
console.log(fibonacci(8)) // 34

// generator 实现方法
function* gen() {
  let [prev, curr] = [0, 1]
  for (;;) { // 如果3个语句全部省略循环体永远循环下去
    [prev, curr] = [curr, curr + prev] // 解构赋值,用当前值替换上一个的值,当前值为上一个值+上一个当前值。很绕
    yield curr // 输出当前数值
  }
}

for (let n of gen()) {
  if (n > 1000) {
    break
  }
  console.log(n)
}
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
// 55
// 89
// 144
// 233
// 377
// 610
// 987

2、用 Generator 给自定义数据结构写一个遍历器

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值