我可以使用ES6的箭头函数语法和生成器吗?(箭头符号)

369

也就是说,我应该如何表达呢?

function *(next) {}

使用箭头语法?我已经尝试了我能想到的所有组合,但找不到任何相关文档。

(我目前正在使用Node.js v0.11.14。)


6
抱歉,你不能这样做。 "function*语句(由函数关键字后跟星号组成)定义了一个生成器函数。" - user663031
4
你希望param*=>{}能做什么? - CoderPi
4
你知道function() {}()=>{}不是做同样的事情吗? - CoderPi
12
“ES6生成器确实是向前迈进了两步,向后退了一步吗?” - 不是的,生成器只能向前迈进 :-) - Bergi
4
我真希望他们在函数的关键字中使用“生成器(generator)”而不是星号,因为星号感觉很随意。 - Costa Michailidis
显示剩余13条评论
8个回答

351

我能在生成器中使用ES6的箭头函数语法吗?

很抱歉,您不能。

根据 MDN

function* 语句(function 关键字后跟一个星号)定义了一个生成器函数。

spec document中可以看到(我的重点):

function 语法被扩展以添加可选的 * 标记:

FunctionDeclaration: "function" "*"? Identifier "(" FormalParameterList? ")" 
  "{" FunctionBody "}"

281
我觉得这似乎是设计缺陷。 - Jonathon
33
箭头函数应该是轻量级的(例如没有 .prototype),通常只有一行代码,而生成器则相反。 - Bergi
62
我已经遇到了几种情况,其中我玩耍的一个生成器需要访问先前的this,并且不得不编写let self = this的方法来在生成器内部获取它。词法作用域+箭头语法会很好用,但很遗憾,这并不是世界末日。 - dvlsg
60
箭头函数背后的推理比那复杂得多。它并不真的是关于简洁性的。箭头函数不需要是轻量级的-确实有一个可选的单语句体语法,但又怎样呢?许多人使用箭头函数来定义除类方法以外的所有函数,并将 function 关键字降为语言中的“坏部分”。这么做有很好的原因。对于这些人来说,箭头生成器的缺失是一个令人烦恼的不一致之处。 - callum
6
@callum,我说的轻量级是指实例创建和调用开销,而不是语法。不确定你认为它们背后的原因是什么。而且,我看不出使用非声明性箭头函数表达式而不是function声明的好理由。 - Bergi
显示剩余5条评论

166

内联函数和箭头函数的区别

首先,箭头函数 () => {} 不是用来替代内联函数 function(){} 的,它们是不同的。内联函数只是一个普通函数,所以问题在于箭头函数和内联函数的区别。

箭头函数表达式(也称为箭头函数)与函数表达式相比具有更短的语法,并且不绑定其自己的thisargumentssupernew.target)。箭头函数总是匿名的。

更多详细信息在此处


为什么箭头函数不能用作生成器

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

使用yield关键字

yield关键字不能在箭头函数的主体中使用(除非在更深层嵌套的函数中允许)。因此,箭头函数不能用作生成器。

请注意,没有yield生成器是没有意义的。


为什么箭头函数不能使用yield。

http://tc39wiki.calculist.org/es6/arrow-functions/

箭头函数在词法上绑定了this,在Block体中绑定return,因此它从立即封闭的箭头函数返回,并且防止breakcontinue引用立即封闭的箭头函数之外的语句。

Identifier主表达式arguments不能在箭头函数的主体中使用(无论是表达式还是块形式)。

同样,在箭头函数的主体中不能使用yield。箭头函数不能作为生成器,并且我们不想要深层次的连续性。

在箭头函数中使用yield将抛出语义错误:http://www.ecma-international.org/

最后,原因在于ECMA6的实现过于复杂。C#也不允许这样做,原因有些类似reasons


101
我正在努力弄清楚为什么 *() => { yield bla; } 不可以,而 async () => { await bla; } 可以…… - Lee Benson
10
@CodeiSir,关于“我们不想要深层次的连续性”这个说辞,很烂的借口。 - Pacerier
38
你的论点具有循环性。你说箭头函数不能成为生成器,因为它们里面不能有yield关键字。但是它们不能有yield关键字,因为它们不能成为生成器:“箭头函数不能成为生成器,我们也不需要深层次的延续。” - Thayne
17
这是循环推理;箭头函数无法成为生成器,因为它不允许有 yield 语句,而它也无法有 yield 语句,因为它不能成为生成器。 - Sapphire_Brick
12
这并没有真正回答“为什么”的问题。没错,箭头函数不能成为生成器,因为它不允许包含 yield,但是语法为什么不能被设计成允许呢?为什么设计者不希望箭头函数能够成为生成器? - chharvey
显示剩余9条评论

61
除了上面提到的在 esdiscuss.org2013年11月Ecma TC39委员会ES6会议记录 上讨论之外,生成器箭头在2016年9月的两次ES7会议中得到了重新讨论 [1] [2]。在对各种语法(主要是=*>=>*)的利弊以及缺乏此功能的证明和用例进行讨论后,他们得出结论:
  • 委员会对此有一定的兴趣,但担心该功能对于添加新的语法不够有利
  • 计划在第三天重新审视,看看我们是否可以至少将=>*置于阶段0作为[Domenic Denicola]的异步迭代提案的一部分

生成器箭头的提案已被移动到阶段 1,由 Brendan Eich 和 Domenic Denicola 担任支持者。如上所述,异步迭代在2018年时已经完成并实现

在2019年10月,由 Sergey Rubanov 创建的官方仓库出现,并有更多关于语法和其他细节的讨论。


12

我也有同样的问题,所以来到这里。在阅读了帖子和评论后,我感觉在箭头函数中使用生成器似乎不太清晰:

const generator = () => 2*3; // * implies multiplication
// so, this would be a confusing
const generator = () =>* something; // err, multiplying?
const generator = () =*> ... // err, ^^
const generator = ()*=> ... // err, *=3, still multiplying?
const generator=*()=> ... // err, ^^
const generator = *param => ... //err, "param" is not fixed word

这可能是他们没有在箭头函数中实现生成器的主要原因。


但是,如果我是其中一员,我可能会这样想:

const generator = gen param => ... // hmm, gen indicates a generator
const generator = gen () => ... // ^^

这感觉就像我们有了异步函数:

const asyncFunction = async () => ... // pretty cool

因为普通函数中有async关键字存在,所以箭头函数利用它- async () => 看起来很像 async function()

但是,没有类似gengenerator的关键字,箭头函数不使用它。

总之:

即使他们希望在箭头函数中实现生成器,我认为他们需要重新考虑核心js中的生成器语法:

generator function myfunc() {}
// rather than
function* myfunc() {} // or, function *myfunc() {}

这将是一个很大的错误。因此,将箭头函数从生成器中排除出去,非常不错。


根据@Bergi的评论:

不行。箭头函数应该轻量级(例如没有 .prototype),通常只有一行代码,而生成器则完全相反。

我会说,生成器的使用目的是运行-停止-运行,所以我认为我们不需要关心原型、词法this等内容。


12
也可以考虑一些异国情调的选项,比如 () ~> { yield 'a'; yield 'b'; }。老实说,我就是喜欢波浪号。 - Gershom Maes
5
@Gershom 这就是编程语言(比如Perl)完全出错的方式。 - Sapphire_Brick
2
我觉得解决方案 const someGeneratorFunction = gen() => ... 很漂亮。 - user3025289
我认为最具助记性的语法应该是=>=>,因为普通函数产生一个值,所以它的参数是=> [获取返回值的逻辑],而生成器可能会产生多个值,并且生成器会先运行,然后停止,再次运行。 - mtraceur

9

目前不行,但是在未来可能会实现,因为 TC39 在2019年10月发布了相应提案,它处于第一阶段。


3
我知道现在已经很晚了,但是可能的原因之一可能是语法问题。也许(*() => {})可以工作,但是(9 ** () => {})呢?这是一个箭头函数的9次方,返回NaN,还是一个生成器箭头函数的9倍,也返回NaN?可以使用一些替代语法,如另一个答案中提到的=>*,但也许有人希望保持生成器函数语法的一致性(例如function* () {}{ *genMethod() {} })在实现时。这并不是一个很好的借口,但却是一个原因。

1
对于双星号,我点赞。这里有一位老派的 JavaScript 程序员。谁说老狗学不了新把戏,哈哈。 - Shanimal
他们不这样做的唯一原因是制作解析器很困难。完全有可能,而且不需要在语法上做出任何妥协。 - Sophie McCarrell
@JasonMcCarrell 如果他们真的关心不让解析器过于复杂,那么 Brendan Eich 或许应该在浏览器中加入 Scheme。 - Sapphire_Brick

3
你可以这样做,但不是很好的方式。它没能缩短代码长度而且看起来也不那么美观。请看下面的示例:
function* iterable(arg) {
    yield* [];
}

async function* asyncIterable(arg) {
    yield* [];
}

const arrowIterable = arg => {
    return {
        *[Symbol.iterator]() {
            yield* [];
        },
    };
};

const arrowAsyncIterable = arg => {
    return {
        async *[Symbol.asyncIterator]() {
            yield* [];
        },
    };
};


这可以实现是因为可迭代对象基本上是一个带有 Symbol.iterator 或者 Symbol.asyncIterator 设置为迭代器的对象。生成器是迭代器!

祝愉快!


-7

使用redux-saga有一个不错的解决方案

import { call, all } from 'redux-saga/effects';

function* gen() {
   yield all([].map(() => {
      return call(....);
   }));
}

5
我们如何知道OP正在使用Redux? - Maros

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