From e6b2242298523f713ea2248dc76f50ba99790f4d Mon Sep 17 00:00:00 2001 From: yuhengshen Date: Mon, 2 Mar 2020 14:28:18 +0800 Subject: [PATCH 1/7] Update article.md --- .../1-generators/article.md | 139 +++++++++--------- 1 file changed, 66 insertions(+), 73 deletions(-) diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index 6f89684805..53b78e6f93 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -1,8 +1,8 @@ # Generators -通常情况下,函数都只会返回一个值或者什么也不返回。 +常规函数必定返回一个具体的值或者 `undefined` 。 -Generators 可以按需一个个返回(“yield”)多个值,可以是无限数量个值。它们与 [iterables](info:iterable) 配合使用,可以轻松创建数据流。 +而 Generators 可以按需逐个生成(“yield”)多个值。它们与 [iterables](info:iterable) 配合使用,可以轻松创建数据流。 ## Generator 函数 @@ -18,24 +18,36 @@ function* generateSequence() { } ``` -“generator 函数”这个术语听起来有点误导,因为我们在调用它时候并不会执行代码。相反,他返回一个特殊的对象,我们称为“generator 对象”。 +“generator 函数”与常规函数的运行表现有所不同,当执行“generator 函数”时,它并不直接执行完**函数体**的代码,相对应的是根据一个特殊的对象(“generator 对象”)来管理“generator 函数”的执行流程。 -因此,它是一种“generator 构造器函数”。 +alert一下这个数据结构 ```js -// “generator 函数”创建“generator 对象” +function* generateSequence() { + yield 1; + yield 2; + return 3; +} + +// "generator function"(指 generateSequence()) 创建了一个 "generator 对象" let generator = generateSequence(); +*!* +alert(generator); // [object Generator] +*/!* ``` -`generator` 对象类似于“冻结函数调用(frozen function call)”: +上面的代码中,**函数体**代码还没有开始执行: ![](generateSequence-1.svg) -在创建后,代码在一开始就暂停执行。 +generator 的主要方法是 `next()`。调用它后,就会恢复上面的执行过程直到最近的 `yield ` 语句( `value` 可以省略,默认为 `undefined` )。然后代码再次暂停执行,并将值 `value` 返回给外部代码。 -Generator 的主要方法是 `next()`。调用它后,就会恢复上面的执行过程直到最近的 `yield ` 语句。然后代码再次暂停执行,并将值返回到外部代码。 +`next()`调用结果总是一个包含两个属性的对象: -例如,这里我们创建了 generator 并获取其第一个 yielded 值: +- `value`: “generator 函数”每次**产出(yielded)**的值。(译者注:yield翻译为产出,是为了配合**生产工厂(generator)**的语义。) +- `done`: `true` 表示“generator 函数”已经执行完成,否则为 `false`。 + +举个栗子🌰,下面我们创建一个 generator 并获取其第一个**产出(yielded)**的值: ```js run function* generateSequence() { @@ -53,15 +65,11 @@ let one = generator.next(); alert(JSON.stringify(one)); // {value: 1, done: false} ``` -`next()` 的结果总是一个对象: -- `value`:yielded 值。 -- `done`:如果代码没有执行完,其值为 `false`,否则就是 `true`。 - -截至目前,我们只获得了第一个值: +截至目前,我们只获得了第一个值,函数体停在了第二行: ![](generateSequence-2.svg) -我们再次调用 `generator.next()`。代码恢复执行并返回下一个 `yield`: +再次调用 `generator.next()`。代码恢复执行并返回下一个 `yield`: ```js let two = generator.next(); @@ -71,7 +79,7 @@ alert(JSON.stringify(two)); // {value: 2, done: false} ![](generateSequence-3.svg) -如果我们第三次调用上面代码,代码将会执行到 `return` 语句,此时将会完成这个函数的执行: +如果我们第三次调用上面代码,代码将会执行到 `return` 语句,此时整个**generator 函数体**才执行完成: ```js let three = generator.next(); @@ -81,23 +89,19 @@ alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*} ![](generateSequence-4.svg) -现在,generator 已经执行完成了。我们通过 `done:true` 和处理的最终结果 `value:3` 可以看出来。 - -此时如果再调用 `generator.next()` 将不起任何作用。如果我们还是执行此语句,那么它将会返回相同的对象:`{done: true}`。 - -对于 generator 来说,没有办法去“回滚”它的操作。但是我们可以通过调用 `generateSequence()` 来创建另一个 generator。 +我们通过 `done:true` 可以看出函数执行完成了,此时 `value:3` 作为函数执行的最终结果。 -到目前为止,最重要的是要理解 generator 函数与常规函数不同,generator 函数不运行代码。它们是作为“generator 工厂”。运行 `function*` 返回一个 generator,然后我们调用它获得需要的值。 +再调用 `generator.next()` 将总是返回相同的对象:`{done: true}`,很明显,这个值已经没有什么意义了。 ```smart header="`function* f(…)` 或者 `function *f(…)`?" 这是一个小的书写习惯问题,两者的语法都是正确的。 -但是通常首选第一种语法,因为星号 `*` 表示它是一个 generator 函数,它描述的是函数种类而不是名称,因此它仍应使用 `function` 关键字。 +但是通常首选第一种语法,因为星号 `*` 表示它是一个 generator 函数,它描述的是函数种类而不是名称,因此`*`应该和 `function` 关键字紧贴一起。 ``` ## Generators 是可迭代的 -你可能通过 `next()` 方法了解到 generator 是[可迭代](info:iterable)的。 +看到 `next()` 方法,或许你都猜到了 generator 是[可迭代](info:iterable)的。(译者注:`next()` 是iterator的必要方法) 我们可以通过 `for..of` 循环迭代所有值: @@ -115,11 +119,11 @@ for(let value of generator) { } ``` -这样的方法看上去要比一个个调用 `.next().value` 好得多,不是吗? +`for ... of`写法是不是比 `.next().value` 优雅多了? ……但是请注意:上面的迭代例子中,它先显示 `1`,然后是 `2`。它不会显示 `3`! -这是因为当 `done: true` 时,for-of 循环会忽略最后一个 `value`。因此,如果我们想要通过 `for..of` 循环显示所有结果,我们必须使用 `yield` 而不是 `return` 返回它们: +这是因为当 `done: true` 时,for-of 循环会忽略最后一个 `value`。因此,如果我们想要通过 `for..of` 循环显示所有结果时,我们必须使用 `yield` 而不是 `return`: ```js run function* generateSequence() { @@ -137,7 +141,7 @@ for(let value of generator) { } ``` -当然,由于 generators 是可迭代的,我们可以调用所有相关的函数,例如:spread 操作 `...`: +由于 generators 是可迭代的,我们可以充分发挥ES6中iterator的特性,例如:spread 操作 `...`: ```js run function* generateSequence() { @@ -151,7 +155,7 @@ let sequence = [0, ...generateSequence()]; alert(sequence); // 0, 1, 2, 3 ``` -在上面的代码中,`...generateSequence()` 将 iterable 转换为 item 的数组(关于 spread 操作可以参见相关章节 [](info:rest-parameters-spread-operator#spread-operator))。 +在上面的代码中,`...generateSequence()` 将 可迭代的“generator对象”转换为了一个数组(关于 spread 操作可以参见相关章节 [](info:rest-parameters-spread-operator#spread-operator))。 ## 使用 generator 进行迭代 @@ -167,14 +171,14 @@ let range = { // for..of range 在一开始就调用一次这个方法 [Symbol.iterator]() { // ……它返回 iterator 对象: - // 向前,for..of 仅适用于该对象,请求下一个值 + // 后续的操作中, for..of 将只针对这个 iterator对象,通过不断的调用 next() 来获取下一个值 return { current: this.from, last: this.to, // for..of 在每次迭代的时候都会调用 next() next() { - // 它应该返回对象 {done:.., value :...} 值 + // 根据iterator规范,必须返回特定结构的对象: {done:.., value :...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { @@ -185,28 +189,13 @@ let range = { } }; +// 迭代整个range对象,返回数组[1,2,3,4,5]。 alert([...range]); // 1,2,3,4,5 ``` -使用 generator 来生成可迭代序列更简单,更优雅: +我们可以通过提供一个 generator 作为对象的 `Symbol.iterator` 来使任何对象可被迭代。 -```js run -function* generateSequence(start, end) { - for (let i = start; i <= end; i++) { - yield i; - } -} - -let sequence = [...generateSequence(1,5)]; - -alert(sequence); // 1, 2, 3, 4, 5 -``` - -## 转换 Symbol.iterator 为 generator - -我们可以通过提供一个 generator 作为 `Symbol.iterator` 来向任何自定义对象添加 generator-style 的迭代。 - -这是相同的 `range`,但是使用的是一个更紧凑的 iterator: +以下是使用的另一种更紧凑的写法的 `range` 对象: ```js run let range = { @@ -223,13 +212,13 @@ let range = { alert( [...range] ); // 1,2,3,4,5 ``` -正常工作,因为 `range[Symbol.iterator]()` 现在返回一个 generator,而 generator 方法正是 `for..of` 所期待的: +代码正常工作,因为 `range[Symbol.iterator]()` 现在返回一个 generator,而 generator 方法正是 `for..of` 所期望的: - 它具有 `.next()` 方法 - 它以 `{value: ..., done: true/false}` 的形式返回值 当然,这不是巧合,Generators 被添加到 JavaScript 语言中时也考虑了 iterators,以便更容易实现。 -带有 generator 的最后一个变体比 `range` 的原始可迭代代码简洁得多,并保持了相同的功能。 +带有 generator 的 `range` 对象比的原始可迭代代码简洁得多,但功能一致。 ```smart header="Generators 可能永远 generate 值" 在上面的例子中,我们生成了有限序列,但是我们也可以创建一个生成无限序列的 generator,它可以一直 yield 值。例如,无序的伪随机数序列。 @@ -241,18 +230,26 @@ alert( [...range] ); // 1,2,3,4,5 Generator 组合是 generator 的一个特殊功能,它可以显式地将 generator “嵌入”到一起。 -例如,我们想要生成一个序列: -- 数字 `0..9`(ASCII 可显示字符代码为 48..57), -- 后跟字母 `a..z`(ASCII 可显示字符代码为 65..90) -- 后跟大写字母 `A..Z`(ASCII 可显示字符代码为 97..122) +如下,我们想要生成一个数字序列: -我们可以使用序列,比如通过从中选择字符来创建密码(也可以添加语法字符),但是我们先生成它。 +```js +function* generateSequence(start, end) { + for (let i = start; i <= end; i++) yield i; +} +``` + +接着,我们复用这个函数来生成更加复杂的包含三部分的序列: +- 第一部分为数字 `0..9`(ASCII 可显示字符代码为 48..57), +- 第二部分为大写字母字母 `A..Z`(ASCII 可显示字符代码为 65..90) +- 第三部分为小写字母 `a...z`(ASCII 可显示字符代码为 97..122) -我们已经有了 `function* generateSequence(start, end)`。让我们重复使用它来一个个地传递 3 个序列,它真是我们所需要的。 +我们可以在这个序列中选择字符来创建密码(也可以添加其他特殊字符),现在先编写这个生成器。 -在普通函数中,为了将多个其他函数的结果组合到一起,我们先调用它们,然后将他们的结果存储起来,最后将它们合并到一起。 +在常规函数的调用中,为了组合多个函数的结果,我们需要先依次调用它们,并分别将他们的结果存储起来,最后统一将它们合并到一起。 -对于 generators,我们可以更好地去实现,就像这样: +对于 generators 而言,我们可以使用 `yield*` 这个语法来将一个 generator 嵌入到另一个generator中: + +组合式generator的例子: ```js run function* generateSequence(start, end) { @@ -283,9 +280,9 @@ for(let code of generatePasswordCodes()) { alert(str); // 0..9A..Za..z ``` -示例中的特殊 `yield*` 指令负责组合。它将执行**委托**给另一个 generator。或者简单来说就是 `yield* gen` 迭代 generator `gen` 并显式地将其 yield 结果转发到外部。好像这些值是由外部 generator yield 一样。 +示例中的 `yield*` 指令负责将执行**委托**给另一个 generator。或者简单来说就是 `yield* gen` 迭代了 generator `gen` 并显式地将 `gen` yield 的结果转发到最外部。好像这些值是由外部 generator yield 的一样。 -结果就像是我们从嵌套的 generators 内联的代码一样: +执行结果和我们将嵌套的 generators 中的代码直接内联到外层generator一样: ```js run function* generateSequence(start, end) { @@ -316,19 +313,15 @@ for(let code of generateAlphaNum()) { alert(str); // 0..9A..Za..z ``` -Generator 组合是将一个 generator 流插入到另一个 generator 的自然的方式。 - -即使来自嵌套 generator 的值的流是无限的,它也可以正常工作。它很简单,不需要使用额外的内存来存储中间结果。 - -## “yield” 双向路径(two-way road) +**Generator 组合**是将一个 generator 流自然地插入到另一个 generator 的方式。它不需要使用额外的内存来存储中间结果。 -直到此时,generators 就像“固醇(steroids)上的 iterators”。这就是它经常被使用的方式。 +## “yield” 双向路径(two-way street) -但是实际上,generators 要更强大,更灵活。 +目前看来,generators 和可迭代对象非常相似,仅仅是其产生 value 的语法有所不同。但实际上,generators 更加高效和灵活。 -这是因为 `yield` 是一个双向路径:它不仅向外面返回结果,而且可以传递 generator 内的值。 +这是因为 `yield` 是一个双向路径:它不仅向外面返回结果,而且可以将外部的值传递 generator 内。 -为此,我们应该使用参数 arg 调用 `generator.next(arg)`。这个参数就成了 `yield` 的结果。 +调用 `generator.next(arg)`,我们就将 `arg` 传递到了 generator 内部。这个 `arg` 参数就是 `yield` 的结果。 我们来看一个例子: @@ -364,9 +357,9 @@ generator.next(4); // --> 向 generator 传入结果 setTimeout(() => generator.next(4), 1000); ``` -我们可以看到,与普通函数不同,generators 和调用代码可以通过传递 `next/yield` 中的值来交换结果。 +我们可以看到,与常规函数不同,generators 内部和外部调用环境可以通过 `next/yield` 来传递值,以交换结果。 -为了使事情浅显易懂,我们来看另一个有更多调用的例子: +为了使流程浅显易懂,我们来看另一个有更多调用的例子: ```js run function* gen() { @@ -406,7 +399,7 @@ alert( generator.next(9).done ); // true ……但是它也可以在那里发起(抛出)错误。这很自然,因为错误本身也是一种结果。 -要向 `yield` 传递错误,我们应该调用 `generator.throw(err)`。在那种情况下,`err` 与 `yield` 一起被抛出。 +要向 `yield` 传递错误,我们应该调用 `generator.throw(err)`。然后,`err` 将在对应的 `yield` 那一行被抛出。 例如,`"2 + 2?"` 的 yield 导致一个错误: @@ -454,7 +447,7 @@ try { */!* ``` -如果我们在那里捕获错误,那么像往常一样,它会转到外部代码(如果有的话),如果没有捕获,则会结束脚本。 +通常,如果我们没有在那里捕获错误,它会将错误转到外部代码(如果有的话),如果外部也没有没有捕获,则会结束脚本。 ## 总结 From 10c3e2d45d3c3462c59d4f7865560ad19ea2f2b8 Mon Sep 17 00:00:00 2001 From: yuhengshen Date: Mon, 2 Mar 2020 14:38:48 +0800 Subject: [PATCH 2/7] Update article.md --- 1-js/12-generators-iterators/1-generators/article.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index 53b78e6f93..1b9491d031 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -40,11 +40,11 @@ alert(generator); // [object Generator] ![](generateSequence-1.svg) -generator 的主要方法是 `next()`。调用它后,就会恢复上面的执行过程直到最近的 `yield ` 语句( `value` 可以省略,默认为 `undefined` )。然后代码再次暂停执行,并将值 `value` 返回给外部代码。 +generator 的主要方法是 `next()`。调用它后,就会恢复上面的执行过程直到最近的 `yield ` 语句( `value` 可以省略,默认为 `undefined` )。然后代码再次暂停执行,并将值返回给外部代码。 `next()`调用结果总是一个包含两个属性的对象: -- `value`: “generator 函数”每次**产出(yielded)**的值。(译者注:yield翻译为产出,是为了配合**生产工厂(generator)**的语义。) +- `value`: “generator 函数”每次 **产出(yielded)** 的值。(译者注:yield翻译为产出,是为了配合 **生产工厂(generator)** 的语义。) - `done`: `true` 表示“generator 函数”已经执行完成,否则为 `false`。 举个栗子🌰,下面我们创建一个 generator 并获取其第一个**产出(yielded)**的值: @@ -79,7 +79,7 @@ alert(JSON.stringify(two)); // {value: 2, done: false} ![](generateSequence-3.svg) -如果我们第三次调用上面代码,代码将会执行到 `return` 语句,此时整个**generator 函数体**才执行完成: +如果我们第三次调用上面代码,代码将会执行到 `return` 语句,此时将会完成这个函数的执行: ```js let three = generator.next(); @@ -319,7 +319,7 @@ alert(str); // 0..9A..Za..z 目前看来,generators 和可迭代对象非常相似,仅仅是其产生 value 的语法有所不同。但实际上,generators 更加高效和灵活。 -这是因为 `yield` 是一个双向路径:它不仅向外面返回结果,而且可以将外部的值传递 generator 内。 +这是因为 `yield` 是一个双向路径:它不仅向外面返回结果,而且可以将外部的值传递到 generator 内。 调用 `generator.next(arg)`,我们就将 `arg` 传递到了 generator 内部。这个 `arg` 参数就是 `yield` 的结果。 From e52c7603cbaf9ea9a35234449ce8e36880b8607b Mon Sep 17 00:00:00 2001 From: yuhengshen Date: Wed, 4 Mar 2020 09:37:18 +0800 Subject: [PATCH 3/7] Apply suggestions from code review Co-Authored-By: Martin --- .../1-generators/article.md | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index 1b9491d031..be3f26490b 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -1,6 +1,6 @@ # Generators -常规函数必定返回一个具体的值或者 `undefined` 。 +常规函数只会返回一个具体值(或者 `undefined`)。 而 Generators 可以按需逐个生成(“yield”)多个值。它们与 [iterables](info:iterable) 配合使用,可以轻松创建数据流。 @@ -18,9 +18,9 @@ function* generateSequence() { } ``` -“generator 函数”与常规函数的运行表现有所不同,当执行“generator 函数”时,它并不直接执行完**函数体**的代码,相对应的是根据一个特殊的对象(“generator 对象”)来管理“generator 函数”的执行流程。 +“generator 函数”与常规函数的运行表现有所不同,当执行“generator 函数”时,它并不直接执行完**函数体**的代码,而是返回一个特殊的对象,即“generator 对象”,来管理执行流程。 -alert一下这个数据结构 +来打印一下这种对象: ```js function* generateSequence() { @@ -40,14 +40,14 @@ alert(generator); // [object Generator] ![](generateSequence-1.svg) -generator 的主要方法是 `next()`。调用它后,就会恢复上面的执行过程直到最近的 `yield ` 语句( `value` 可以省略,默认为 `undefined` )。然后代码再次暂停执行,并将值返回给外部代码。 +generator 对象的主要方法是 `next()`。被调用时,它会恢复上面的执行过程直到最近的 `yield ` 语句( `value` 可以省略,默认为 `undefined` )。然后代码再次暂停执行,并将值返回给外部代码。 -`next()`调用结果总是一个包含两个属性的对象: +`next()` 调用结果总是一个包含两个属性的对象: - `value`: “generator 函数”每次 **产出(yielded)** 的值。(译者注:yield翻译为产出,是为了配合 **生产工厂(generator)** 的语义。) - `done`: `true` 表示“generator 函数”已经执行完成,否则为 `false`。 -举个栗子🌰,下面我们创建一个 generator 并获取其第一个**产出(yielded)**的值: +举个例子,下面我们创建一个 generator 并获取其第一个产出的值: ```js run function* generateSequence() { @@ -69,7 +69,7 @@ alert(JSON.stringify(one)); // {value: 1, done: false} ![](generateSequence-2.svg) -再次调用 `generator.next()`。代码恢复执行并返回下一个 `yield`: +再次调用 `generator.next()`。代码恢复执行并返回下一个 `yield` 的产出值: ```js let two = generator.next(); @@ -91,7 +91,7 @@ alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*} 我们通过 `done:true` 可以看出函数执行完成了,此时 `value:3` 作为函数执行的最终结果。 -再调用 `generator.next()` 将总是返回相同的对象:`{done: true}`,很明显,这个值已经没有什么意义了。 +再调用 `generator.next()` 已经没有什么意义了。这将总是返回相同的对象:`{done: true}`。 ```smart header="`function* f(…)` 或者 `function *f(…)`?" 这是一个小的书写习惯问题,两者的语法都是正确的。 @@ -101,7 +101,7 @@ alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*} ## Generators 是可迭代的 -看到 `next()` 方法,或许你都猜到了 generator 是[可迭代](info:iterable)的。(译者注:`next()` 是iterator的必要方法) +看到 `next()` 方法,或许你都猜到了 generator 是 [可迭代](info:iterable) 的。(译者注:`next()` 是 iterator 的必要方法) 我们可以通过 `for..of` 循环迭代所有值: @@ -141,7 +141,7 @@ for(let value of generator) { } ``` -由于 generators 是可迭代的,我们可以充分发挥ES6中iterator的特性,例如:spread 操作 `...`: +由于 generators 是可迭代的,我们可以充分发挥 ES6 中 iterator 的特性,例如:spread 操作 `...`: ```js run function* generateSequence() { @@ -155,7 +155,7 @@ let sequence = [0, ...generateSequence()]; alert(sequence); // 0, 1, 2, 3 ``` -在上面的代码中,`...generateSequence()` 将 可迭代的“generator对象”转换为了一个数组(关于 spread 操作可以参见相关章节 [](info:rest-parameters-spread-operator#spread-operator))。 +在上面的代码中,`...generateSequence()` 将可迭代的“generator对象”转换为了一个数组(关于 spread 操作可以参见相关章节 [](info:rest-parameters-spread-operator#spread-operator))。 ## 使用 generator 进行迭代 @@ -171,14 +171,14 @@ let range = { // for..of range 在一开始就调用一次这个方法 [Symbol.iterator]() { // ……它返回 iterator 对象: - // 后续的操作中, for..of 将只针对这个 iterator对象,通过不断的调用 next() 来获取下一个值 + // 后续的操作中, for..of 将只针对这个 iterator 对象,通过不断的调用 next() 来获取下一个值 return { current: this.from, last: this.to, // for..of 在每次迭代的时候都会调用 next() next() { - // 根据iterator规范,必须返回特定结构的对象: {done:.., value :...} + // 必须返回特定结构的对象: {done:.., value :...} if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { @@ -189,11 +189,11 @@ let range = { } }; -// 迭代整个range对象,返回数组[1,2,3,4,5]。 +// 迭代整个 range 对象,返回从 `range.from` 到 `range.to` 范围的所有数字的数组。 alert([...range]); // 1,2,3,4,5 ``` -我们可以通过提供一个 generator 作为对象的 `Symbol.iterator` 来使任何对象可被迭代。 +我们可以通过提供一个 generator 函数作为对象的 `Symbol.iterator` 来使任何对象可被迭代。 以下是使用的另一种更紧凑的写法的 `range` 对象: @@ -218,9 +218,9 @@ alert( [...range] ); // 1,2,3,4,5 当然,这不是巧合,Generators 被添加到 JavaScript 语言中时也考虑了 iterators,以便更容易实现。 -带有 generator 的 `range` 对象比的原始可迭代代码简洁得多,但功能一致。 +带有 generator 的 `range` 对象比的原始可迭代代码简洁得多,还保持了功能的一致。 -```smart header="Generators 可能永远 generate 值" +```smart header="Generators 可以永远产生值" 在上面的例子中,我们生成了有限序列,但是我们也可以创建一个生成无限序列的 generator,它可以一直 yield 值。例如,无序的伪随机数序列。 这种情况下的 `for..of` generator 需要一个 `break`(或者 `return`)语句,否则循环将永远重复并挂起。 @@ -230,7 +230,7 @@ alert( [...range] ); // 1,2,3,4,5 Generator 组合是 generator 的一个特殊功能,它可以显式地将 generator “嵌入”到一起。 -如下,我们想要生成一个数字序列: +如下,我们有个生成数字序列的函数: ```js function* generateSequence(start, end) { @@ -247,9 +247,9 @@ function* generateSequence(start, end) { 在常规函数的调用中,为了组合多个函数的结果,我们需要先依次调用它们,并分别将他们的结果存储起来,最后统一将它们合并到一起。 -对于 generators 而言,我们可以使用 `yield*` 这个语法来将一个 generator 嵌入到另一个generator中: +对于 generators 而言,我们可以使用 `yield*` 这个语法来将一个 generator 嵌入(组合)到另一个 generator 中: -组合式generator的例子: +组合式 generator 的例子: ```js run function* generateSequence(start, end) { @@ -280,7 +280,7 @@ for(let code of generatePasswordCodes()) { alert(str); // 0..9A..Za..z ``` -示例中的 `yield*` 指令负责将执行**委托**给另一个 generator。或者简单来说就是 `yield* gen` 迭代了 generator `gen` 并显式地将 `gen` yield 的结果转发到最外部。好像这些值是由外部 generator yield 的一样。 +示例中的 `yield*` 指令负责将执行**委托**给另一个 generator。或者简单来说就是 `yield* gen` 迭代了名为 `gen` 的 generator 并显式地将 `gen` yield 的结果转发到最外部。好像这些值是由外部的 generator yield 的一样。 执行结果和我们将嵌套的 generators 中的代码直接内联到外层generator一样: @@ -321,7 +321,7 @@ alert(str); // 0..9A..Za..z 这是因为 `yield` 是一个双向路径:它不仅向外面返回结果,而且可以将外部的值传递到 generator 内。 -调用 `generator.next(arg)`,我们就将 `arg` 传递到了 generator 内部。这个 `arg` 参数就是 `yield` 的结果。 +调用 `generator.next(arg)`,我们就能将值 `arg` 传递到了 generator 内部。这个 `arg` 参数会变成 `yield` 语句的结果(返回值)。 我们来看一个例子: @@ -359,7 +359,7 @@ setTimeout(() => generator.next(4), 1000); 我们可以看到,与常规函数不同,generators 内部和外部调用环境可以通过 `next/yield` 来传递值,以交换结果。 -为了使流程浅显易懂,我们来看另一个有更多调用的例子: +为了使以上要点浅显易懂,我们来看另一个有更多调用的例子: ```js run function* gen() { From 94b46e51e7b910fc721c54ba86ec827ea67e0636 Mon Sep 17 00:00:00 2001 From: yuhengshen Date: Wed, 4 Mar 2020 09:41:08 +0800 Subject: [PATCH 4/7] Apply suggestions from code review --- 1-js/12-generators-iterators/1-generators/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index be3f26490b..f42d0c2387 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -44,7 +44,7 @@ generator 对象的主要方法是 `next()`。被调用时,它会恢复上面 `next()` 调用结果总是一个包含两个属性的对象: -- `value`: “generator 函数”每次 **产出(yielded)** 的值。(译者注:yield翻译为产出,是为了配合 **生产工厂(generator)** 的语义。) +- `value`: “generator 函数”每次 **产出(yielded)** 的值。(译者注:yield翻译为产出,是为了配合 **生成器(generator)** 的语义。) - `done`: `true` 表示“generator 函数”已经执行完成,否则为 `false`。 举个例子,下面我们创建一个 generator 并获取其第一个产出的值: @@ -447,7 +447,7 @@ try { */!* ``` -通常,如果我们没有在那里捕获错误,它会将错误转到外部代码(如果有的话),如果外部也没有没有捕获,则会结束脚本。 +通常,如果我们没有在那里捕获错误,它会将错误转到外部代码(如果有的话),如果外部也没有捕获错误,则会结束脚本。 ## 总结 From 26e1857d3ab67381faaa25a23c4af45cae781091 Mon Sep 17 00:00:00 2001 From: yuhengshen Date: Wed, 4 Mar 2020 09:47:24 +0800 Subject: [PATCH 5/7] Update 1-js/12-generators-iterators/1-generators/article.md --- 1-js/12-generators-iterators/1-generators/article.md | 1 - 1 file changed, 1 deletion(-) diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index f42d0c2387..3f60f9ee28 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -43,7 +43,6 @@ alert(generator); // [object Generator] generator 对象的主要方法是 `next()`。被调用时,它会恢复上面的执行过程直到最近的 `yield ` 语句( `value` 可以省略,默认为 `undefined` )。然后代码再次暂停执行,并将值返回给外部代码。 `next()` 调用结果总是一个包含两个属性的对象: - - `value`: “generator 函数”每次 **产出(yielded)** 的值。(译者注:yield翻译为产出,是为了配合 **生成器(generator)** 的语义。) - `done`: `true` 表示“generator 函数”已经执行完成,否则为 `false`。 From d8c7c6484bb316471c604c146fab707ec92ca283 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 5 Mar 2020 10:18:43 +0800 Subject: [PATCH 6/7] Update 1-js/12-generators-iterators/1-generators/article.md --- 1-js/12-generators-iterators/1-generators/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index 3f60f9ee28..6847773bf3 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -118,7 +118,7 @@ for(let value of generator) { } ``` -`for ... of`写法是不是比 `.next().value` 优雅多了? +`for ... of` 写法是不是比 `.next().value` 优雅多了? ……但是请注意:上面的迭代例子中,它先显示 `1`,然后是 `2`。它不会显示 `3`! From 243553a3aae1813f69ee5b754f50d885554c3061 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 5 Mar 2020 10:19:02 +0800 Subject: [PATCH 7/7] Update 1-js/12-generators-iterators/1-generators/article.md --- 1-js/12-generators-iterators/1-generators/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md index 6847773bf3..b1a7980ff7 100644 --- a/1-js/12-generators-iterators/1-generators/article.md +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -154,7 +154,7 @@ let sequence = [0, ...generateSequence()]; alert(sequence); // 0, 1, 2, 3 ``` -在上面的代码中,`...generateSequence()` 将可迭代的“generator对象”转换为了一个数组(关于 spread 操作可以参见相关章节 [](info:rest-parameters-spread-operator#spread-operator))。 +在上面的代码中,`...generateSequence()` 将可迭代的“generator 对象”转换为了一个数组(关于 spread 操作可以参见相关章节 [](info:rest-parameters-spread-operator#spread-operator))。 ## 使用 generator 进行迭代