JavaScript中的“function*”是什么?

269

我在这个页面上发现了一种新的 JavaScript 函数类型:

// NOTE: "function*" is not supported yet in Firefox.
// Remove the asterisk in order for this code to work in Firefox 13 

function* fibonacci() { // !!! this is the interesting line !!!
    let [prev, curr] = [0, 1];
    for (;;) {
        [prev, curr] = [curr, prev + curr];
        yield curr;
    }
}

我已经知道 yield、let 和 [?,?]=[?,?] 做什么了,但不知道 function* 是什么意思。它是什么?

另外,不要费心在 Google 上搜索,使用星号作为占位符 (通配符),因此无法搜索带有星号的表达式。


6
例子中的注释现在相当陈旧了,自Firefox v26起支持function*语法:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* 。旧版本使用不同的语法。 - Nickolay
50
关于谷歌,只需搜索“function star”或“function asterisk”。这就是我发现这个问题的方法 ;). - trysis
2
看起来@Nickolay的链接中的*被去掉了。这里是一个指向MDN上function*的链接(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)。确实,自v26以来有“基本”支持(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*#Browser_compatibility)。 - ruffin
另一个 MDN 链接(顺便说一下,我在 OP 链接的 MDN 页面上找到了它):https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Generator - BlueRaja - Danny Pflughoeft
关于搜索这个问题,有一个搜索引擎叫做SymbolHound,它可以包含你搜索中的特殊字符。 - Eats Indigo
显示剩余2条评论
4个回答

227

这是一个Generator函数。

生成器函数可以被退出并稍后重新进入。它们的上下文(变量绑定)将在重新进入时保存。

调用生成器函数不会立即执行其主体;而是返回该函数的迭代器对象。当调用迭代器的 next() 方法时,生成器函数的主体将被执行,直到第一个 yield 表达式,该表达式指定要从迭代器返回的值,或者使用 yield* 将控制权委托给另一个生成器函数。


历史注释:

这是一个提议的语法,用于 EcmaScript.next

Mozilla 的 Dave Herman 发表了一篇关于 EcmaScript.next 的演讲。在 30:15 他谈到了生成器。

早些时候,他解释了 Mozilla 正在实验性地实现提议的语言更改以帮助指导委员会。Dave 与 Brendan Eich 密切合作,后者是 Mozilla 的 CTO(我想),也是最初的 JavaScript 设计师。

您可以在 EcmaScript 工作组维基上找到更多详细信息:http://wiki.ecmascript.org/doku.php?id=harmony:generators

工作组(TC-39)普遍认为,EcmaScript.next 应该有某种生成器迭代器提案,但这还没有最终确定。

您不应该指望在语言的下一个版本中不做任何改变地看到这个,即使没有改变,其他浏览器也可能一段时间内不会广泛支持。

Overview

First-class coroutines, represented as objects encapsulating suspended execution contexts (i.e., function activations). Prior art: Python, Icon, Lua, Scheme, Smalltalk.

Examples

The “infinite” sequence of Fibonacci numbers (notwithstanding behavior around 253):

function* fibonacci() {
    let [prev, curr] = [0, 1];
    for (;;) {
        [prev, curr] = [curr, prev + curr];
        yield curr;
    }
}

Generators can be iterated over in loops:

for (n of fibonacci()) {
    // truncate the sequence at 1000
    if (n > 1000)
        break;
    print(n);
}

Generators are iterators:

let seq = fibonacci();
print(seq.next()); // 1
print(seq.next()); // 2
print(seq.next()); // 3
print(seq.next()); // 5
print(seq.next()); // 8

8
追踪问题:没有参数的for循环(for(;;))是什么意思?为什么在这个上下文中使用它?这个for循环没有参数,它会一直重复执行包含在循环体内的代码,直到遇到break语句才会停止。在某些情况下,使用这种循环结构可以更简洁地实现需要无限循环直到条件满足才退出的程序逻辑。 - Fergie
15
@Fergie,“for(;;)”和“while(true)”是相同的。在这个上下文中使用它是因为斐波那契数列是一个无限序列。我会翻译成:“@Fergie,'for(;;)'与'while(true)'相同。在这种情况下使用它是因为斐波那契数列是一个无限序列。” - Mike Samuel
3
@DaveVandenEynde,先前的先前技术:Python yield。先前的先前的先前技术:CLU和Icon。 - Mike Samuel

55

这是一个生成器函数 - 并且它在你引用的页面中已经说明,在你替换成“这是有趣的那一行”的评论中......

基本上,这是一种以编程方式指定序列的方法,以便可以在不先计算整个序列(可能是无限大的)的情况下传递和访问元素索引。


11
“通过索引访问,而不必计算整个序列”可能是我目前为止遇到的最有用的关于生成器的解释。我可以看到在应用程序中使用它,而以前仅仅是理论上的理解。 - wes

11
function*类型看起来像是用于可迭代的进程生成器函数。C# 中有类似的特性,使用 "yield return" 参见1参见2。实质上,这会一个接一个地返回每个值给正在迭代该函数的内容,这也是为什么它们的用例中显示了使用 foreach 风格的循环。

-1

function*

调用生成器函数不会立即执行其主体;而是返回一个函数的迭代器对象。当调用迭代器的next()方法时,生成器函数的主体被执行,直到第一个yield表达式,该表达式指定要从迭代器返回的值或使用yield*委托给另一个生成器函数。next()方法返回一个对象,其中包含一个值属性,该属性包含生成器产生的值,并且done属性指示生成器是否已经产生了最后一个值,作为布尔值。使用参数调用next()方法将恢复生成器函数的执行,将暂停执行的yield表达式替换为next()方法中的参数。 文档:MDN

function* generator(i) {
  yield i;
  yield i + 10;
}

const gen = generator(10);

console.log(gen.next().value);
// expected output: 10

console.log(gen.next().value);
// expected output: 20


谢谢,但我认为更多的文本解释会很有用。 - dawn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接