ES6 - 初学者指南 - 生成器

2025-06-10

ES6 - 初学者指南 - 生成器

预告:这个话题很难!我花了一周时间才开始理解。你可能也得读几遍,我花了无数时间看YouTube上的相关视频。最后我会附上我最喜欢的一个视频的链接。

你在说什么?

生成器!ES6 的一项功能,本质上只是一个用于迭代一系列值的函数。然而,它还有一个额外的功能!是什么呢?我听到你问了。好吧,让我试着解释一下,使用生成器时,你可以暂停代码执行来执行某些操作,然后稍后在另一个 clode 块中返回执行。它们一开始可能很吓人,但过一段时间就很容易理解了。对我来说,最难的是找到一个或几个例子,让我稍微了解一下它们为什么有用,以及为什么它们比其他可能的工作方式更好(如果我能找到同时展示两种工作方式的例子就太棒了!)。通常我会先看看 ES5 的工作方式,这次我要稍微改变一下,我们将从 ES6 的方式开始!

那么发电机是什么样子的

function * numberGenerator() {
    yield 1
    yield 2
    yield 3
}
Enter fullscreen mode Exit fullscreen mode

注意*function 关键字后面的 ,它告诉我们这是一个生成器函数。然后我们有一个新的关键字,这个关键字在函数内部yield被视为 的迷你版本。return

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

const myNumbers = numberGenerator()
Enter fullscreen mode Exit fullscreen mode

当你像上面那样调用一个生成器时,它不会开始执行任何操作,而是处于暂停状态,并返回一个生成器对象。生成器对象内部有 3 个原型,分别称为next()return()throw()。我们先来看一下next()原型。

请输入 next()!

当我们调用下一个原型时,本质上我们所做的就是告诉生成器函数启动并运行,直到遇到 yield 关键字,让我们看一下示例:

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

const myNumbers = numberGenerator()
console.log(myNumbers.next()) // This will return { value: 1, done: false } in a console log
Enter fullscreen mode Exit fullscreen mode

这里我们看到我们的代码已经启动并运行到第一个 yield 1。它的输出给我们一个具有 value 属性和 done 属性的对象,done 属性将一直为 false,直到看到最后一个 yield 语句

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

const myNumbers = numberGenerator()
console.log(myNumbers.next()) // This will return { value: 1, done: false } in a console log
console.log(myNumbers.next()) // This will return { value: 2, done: false } in a console log
console.log(myNumbers.next()) // This will return { value: 3, done: false } in a console log
console.log(myNumbers.next()) // This will return { value: undefined, done: true } in a console log
Enter fullscreen mode Exit fullscreen mode

上面我们看到,在执行完所有 yield 之后,我们看到了 undefined 的值,而 done 的值为 true。为了使代码执行过程更清晰一些,我们可以在生成器中添加一些日志消息:

function * numberGenerator() {
    console.log('Before 1')
    yield 1
    console.log('Before 2')
    yield 2
    console.log('Before 3')
    yield 3
    console.log('After 3')
}

const myNumbers = numberGenerator()
// We will see a console log stating "Before 1"
console.log(myNumbers.next()) // This will return { value: 1, done: false } in a console log
// We will see a console log stating "Before 2"
console.log(myNumbers.next()) // This will return { value: 2, done: false } in a console log
// We will see a console log stating "Before 3"
console.log(myNumbers.next()) // This will return { value: 3, done: false } in a console log
// We will see a console log stating "After 3"
console.log(myNumbers.next()) // This will return { value: undefined, done: true } in a console log
Enter fullscreen mode Exit fullscreen mode

上面的内容让我们更清楚地理解,当我们第一次调用时.next(),我们将进入我们的函数,并执行直到第一个yield,所以我们将输出Before 1,然后{ value: 1, done: false}等等。

那么有些用例怎么样?

在写这篇文章之前,我想尝试找到一些例子来巩固我对这个主题的理解,老实说,我不能说我完全理解它,但我们在这里尝试一下,也许你可以帮助我理解更多的用例?

生成用户 ID

function* generateId() {
  let id = 1 // We could take this number from a database lookup

  while (true) {
    yield id
    id++
  }
}

const gen = generateId()
console.log(gen.next().value) // This would return 1 in a console log
console.log(gen.next().value) // This would return 2 in a console log
console.log(gen.next().value) // This would return 3 in a console log
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,我们使用 while 循环使生成器成为一个无限循环,始终生成下一个数字。这样做的好处是什么?好吧,如果你尝试while(true)在自己的代码中运行循环,浏览器会在几秒钟内崩溃,而阻止它的唯一方法就是终止电脑上的浏览器进程(千万不要尝试!),而在生成器中这样做意味着我们一次只能执行一步。

我可以传递参数吗?

是的,你可以将参数传递给生成器的 next() 函数,我必须承认这一点让我困惑了一段时间。简而言之,你可以传入一个参数,但是如果这是第一次调用,.next()它将不会有任何效果,因为你还没有 yield 任何东西。传递给 next() 的参数.next()实际上会替换之前 yield 的参数。我将尝试用下面的代码示例来解释:

const maxScore = 5;

function* keepScore() {
  let score = 0;

  while (true) {
    const addToScore = yield score // the first call to .next() will only run to here therefore returning 0
    if(addToScore) { // addToScore is populated by the parameter you pass in after the first run
      score += addToScore
    }
  }
}

const playerOne = keepScore()
console.log('score after first move: ')
console.log(playerOne.next()) // This would output 0
console.log('score after second move: ')
console.log(playerOne.next(3)) // This would output 3
console.log('score after third move: ')
console.log(playerOne.next(2)) // This would output 5
console.log('score after fourth move: ')
console.log(playerOne.next()) // This would output 5
console.log('score after fifth move: ')
console.log(playerOne.next(6)) // This would output 11
Enter fullscreen mode Exit fullscreen mode

提前退出

使用生成器可以退出函数,这可以通过两种方式之一实现。首先,你可以使用 call.return()而不是 next 来退出生成器,或者你可以return在生成器函数内部使用语句。例如:

const maxCount = 50;
let hitMax = false;

function* countUp() {
    let count = 0

  while (true) {
    const addToCount = yield count
    if(addToCount) {
      count += addToCount;
    }
    if(count >= maxCount){
      hitMax = true;
      return `maxCount has been hit or exceeded` 
    }
  }
}

const counting = countUp();
counting.next();
for(let i=0; !hitMax; i++){
console.log(counting.next(i));
}
console.log("I am done")
Enter fullscreen mode Exit fullscreen mode

上面我们会一直计数,直到hitMax结果为真,然后停止并退出for循环,在这个例子中,我们返回到生成器内部。让我们看一个替代方案:

const maxCount = 50;
let hitMax = false;

function* countUp() {
    let count = 0

  while (true) {
    const addToCount = yield count
    if(addToCount) {
      count += addToCount;
    }
    if(count >= maxCount){
      hitMax = true;
    }
  }
}

const counting = countUp();
counting.next();
for(let i=0; !counting.next().done; i++){
  if(!hitMax){
    console.log(counting.next(i));
  } else {
    console.log(counting.return('maxCount has been hit or exceeded'))
  }
}
console.log("I am done")
Enter fullscreen mode Exit fullscreen mode

上面我们必须稍微不同地工作,我们将继续递增,直到done的值为.next()真,在循环内我们检查布尔值hitMax,如果我们命中了它而不是再次计数,我们将调用.return('maxCount has been hit or exceeded')它将.next().done值设置为真并允许我们输出“完成”消息。

概述

哇!这是迄今为止我研究过的最难的课题,我想我至少已经基本理解了它。我发现最大的挑战是找到并理解现实世界的用例。我仍然觉得我没有完全理解这个例子,也许你有更好的方法?如果你有,欢迎在评论区分享例子 :) 我从中学到的最重要的一点是:

生成器允许你多次退出并重新进入函数,直到 done 值为 true,这意味着你不需要在生命周期的各个阶段调用多个函数。如果你有更好的解释,请联系我!

12 分钟学会 JavaScript 生成器 - Web 开发简化

鏂囩珷鏉ユ簮锛�https://dev.to/stefanwrightcodes/es6-a-beginners-guide-generators-48hj
PREV
Glitch 如何教会我 Node.js 堆栈
NEXT
使用 Laravel 作为后端在 Nuxt SPA 中进行安全身份验证