如何在ES2015中编写命名箭头功能?


204

我有一个函数要转换为ES6中的新箭头语法。它是一个命名函数:

function sayHello(name) {
    console.log(name + ' says hello');
}

有没有办法在不使用var语句的情况下为其命名:

var sayHello = (name) => {
    console.log(name + ' says hello');
}

显然,我只能在定义此功能后使用它。如下所示:

sayHello = (name) => {
        console.log(name + ' says hello');
    }

ES6中是否有新方法可以做到这一点?


3
箭头语法的重点是给函数命名吗?
AstroCB 2015年

64
箭头函数不一定会保持词法范围,因此,用速记来产生具有词法范围的命名函数(对堆栈跟踪有用)会非常有用
Matt Styles

6
第二个片段出了什么问题?
Bergi 2015年

您当然可以在函数体内引用一个分配的名称:var sayHello =(name)=> {console.log(sayHello); }; 而且对于递归函数,您通常会做到这一点。这不是一个超级有用的示例,但是如果没有它的参数,这是一个返回自身的函数:var sayHello =(name)=> name?console.log(name +'say hello'):sayHello; sayHello()('frank'); //->“坦率地说你好”
迪普森

4
问题的标题与其内容相比具有极大的误导性,因为您已经排除了箭头功能的命名方式(这是第二个代码段)。
TJ Crowder

Answers:


211

如何在ES2015中编写命名箭头功能?

您可以按照在问题中排除的方式进行操作:将其放在赋值或属性初始化程序的右侧,在该赋值或属性初始化程序中,JavaScript引擎可以合理地将变量或属性名称用作名称。没有其他方法可以执行此操作,但是这样做是正确的,并且在规范中已完全涵盖。

根据规范,此函数的真实名称为sayHello

var sayHello = name => {
    console.log(name + ' says hello');
};

这在“ 赋值运算符”>“运行时语义:评估”中定义,在其中调用抽象SetFunctionName操作(该调用当前在步骤1.e.iii中)。

类似地,运行时语义:PropertyDefinitionEvaluation调用SetFunctionName,因此为该函数提供了真实的名称:

let o = {
    sayHello: name => {
        console.log(`${name} says hello`);
    }
};

现代引擎已经为类似的语句设置了函数的内部名称。Edge仍然具有在name运行时标志后面的功能实例上使其可用的功能。

例如,在Chrome或Firefox中,打开Web控制台,然后运行以下代码段:

"use strict";
let foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

在Chrome 51及更高版本和Firefox 53及更高版本(以及带有实验性标记的Edge 13及更高版本)上,运行该代码时,您会看到:

foo.name是:foo
错误
    在foo(http://stacksnippets.net/js:14:23)
    在http://stacksnippets.net/js:17:3

注意foo.name is: fooError...at foo

在Chrome 50和更早的版本,Firefox 52和更早的版本以及没有实验标记的Edge上,您会看到它,因为它们还没有此Function#name属性:

foo.name是: 
错误
    在foo(http://stacksnippets.net/js:14:23)
    在http://stacksnippets.net/js:17:3

需要注意的是名字是从丢失foo.name is:,但它在堆栈跟踪中。只是,实际上在功能上实现name 属性的优先级低于某些其他ES2015功能;Chrome和Firefox现在拥有它;Edge将其放在标志后面,大概不会再落后标志了。

显然,我只能在定义此功能后使用它

正确。没有箭头函数的函数声明语法,只有函数表达式语法,并且没有等效于老式命名函数表达式(var f = function foo() { };)中名称的箭头。因此,它不等于:

console.log(function fact(n) {
    if (n < 0) {
        throw new Error("Not defined for negative numbers");
    }
    return n == 0 ? 1 : n * fact(n - 1);
}(5)); // 120

您必须将其分为两个表达式(我认为您仍然应该这样做)

let fact = n => {
    if (n < 0) {
      throw new Error("Not defined for negative numbers.");
    }
    return n == 0 ? 1 : n * fact(n - 1);
};
console.log(fact(5));

当然,如果必须将其放在需要单个表达式的地方,则始终可以...使用箭头函数:

console.log((() => {
    let fact = n => {
        if (n < 0) {
            throw new Error("Not defined for negative numbers.");
        }
        return n == 0 ? 1 : n * fact(n - 1);
    };
    return fact(5);
})()); // 120

我并不是说那很漂亮,但是如果您绝对需要一个单独的表达式包装器,它就可以工作。


如何防止设置名称(例如在较旧的引擎中)?
trusktr

1
@trusktr:我不知道您为什么要这么做,但是如果您想防止它:不要做上面的事情。例如,虽然let f = () => { /* do something */ };为函数分配名称,let g = (() => () => { /* do something */ })();但没有。
TJ Crowder

那就是我最终要做的。我宁愿我的类继承库生成的匿名类都是匿名的,而不是我的类继承库的用户不需要考虑的内部变量的名称,我希望最终用户看到一个名称仅当他们提供了班级的名称时。(github.com/trusktr/lowclass
trusktr

4
@trusktr:他们所犯的一个例外可能适合您的目的,然后:如果将功能分配给现有对象的属性,则不会设置名称:o.foo = () => {};不会为功能命名。这为了防止信息泄漏。
TJ Crowder

86

否。箭头语法是匿名函数的缩写。匿名函数是匿名的。

命名函数用function关键字定义。


16
正如@DenysSéguret所述,这不是匿名函数的缩写。您将获得一个硬绑定函数。您可以在此处查看转译的结果bit.do/es2015-arrow,以更好地理解其含义...
sminutoli

16
该答案的第一个单词对于所提出的问题是正确的因为OP排除了您命名箭头功能的方式。但是答案的其余部分根本是错误的。在规范中查找使用抽象SetFunctionName操作的位置。根据规范和现代引擎let a = () => {};定义一个命名函数(名称为a)(在其中看到错误);他们只是尚不支持该name物业(但已在规范中,他们最终会到达那里)。
TJ Crowder

4
@DenysSéguret:您可以简单地命名它们,在规范中。您可以按照OP在问题中排除的方式来执行此操作,这将为函数(不仅仅是变量)提供真实的名称。
TJ Crowder

5
@TJCrowder感谢您提供的信息和有用的答案。我不知道这个功能(非常奇怪)。
DenysSéguret16年

4
这个答案是不正确的。问题由以下
@TJCrowder

44

如果用“命名”表示您要.name设置箭头功能的属性,那么您很幸运。

如果在赋值表达式的右侧定义了箭头函数,则引擎将在左侧使用名称,并使用它来设置箭头函数的名称.name,例如

var sayHello = (name) => {
    console.log(name + ' says hello');
}

sayHello.name //=== 'sayHello'

话虽如此,您的问题似乎更多是“我可以将箭头功能提升吗?”。恐怕,答案是“不”。


1
在当前版本的chrome中,至少sayHello.name仍为“”;请注意,就像所有函数一样,sayHello是一个对象,因此您可以根据需要定义任意属性。您不能将“名称”写为只读,但可以说“ Hello.my_name =“ sayHello”;不过,不确定您能得到什么,因为您无法在函数体内进行引用。
Dtipson '16

2
我错了:虽然名称不是直接可变的,但您可以这样配置:Object.defineProperty(sayHello,'name',{value:'sayHello'})
Dtipson

1
“如果在右侧[..]上定义了箭头功能”,其来源是什么?我在规格中找不到它。只需引用引用即可。
朱斯

@Dtipson:那是因为现代引擎尚未实现nameES2015规范要求的任何地方设置属性;他们会做到的。这是Chrome合规性列表上的最后一件事,甚至Chrome 50也没有(Chrome 51最终会提供)。细节。但是,OP专门排除了这样做的可能性。
TJ Crowder

我可以在toString中获得此名称吗?我尝试覆盖它,但是后来我不知道如何访问函数体。
Qwerty

0

看来ES7可以做到这一点:https ://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

给出的示例是:

class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

ES6箭头函数的主体与包围它们的代码共享相同的词法,这是因为ES7属性初始化器的作用域使我们能够获得预期的结果。

请注意,要与babel配合使用,我需要启用最具实验性的ES7阶段0语法。在我的webpack.config.js文件中,我像这样更新了babel加载程序:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},

6
那不完全是一个命名的箭头函数。这是在构造函数中创建实例方法的快捷方式,我想知道它在这里有何意义?
Bergi

@Bergi,您好,我不确定这是否是原始作者的意图,但是我试图创建一个与该对象具有相同作用域的命名函数。如果我们不使用这种技术,而我们想拥有适当的范围,则必须在类构造函数中使用它。this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie 2015年

8
嗯,您可以轻松地使用constructor() { this.handleOptionsButtonClick = (e) => {…}; }它,它完全等同于您的答案中的代码,但已经可以在ES6中使用。无需使用.bind
Bergi 2015年

1
顺便说一句,我认为您将“命名函数”与“(实例)方法”和“范围”与“ this上下文”
混淆了

1
@tdecs:是的,您可以;约格错了。let a = () => {};定义了一个名为的箭头函数a细节
TJ Crowder

0

实际上,看来是一种命名箭头功能的方法(至少从chrome 77起)是这样做的:

"use strict";
let fn_names = {};
fn_names.foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

在此处输入图片说明


理论上可以创建babel宏fn('yourName', ()=>{}),该宏可以保持100%正确的箭头函数语义,或者更好地是babel插件用于“命名的箭头函数语法”:fn yourName() => {}。这些都可以编译为上面的代码。我认为命名箭头会如此BADA55
Devin G Rhode

-1

为了编写名为arrow的函数,您可以在下面的示例中进行介绍,其中有一个名为LoginClass的类,在该类中我编写了一个名为function箭头,名为successAuth class LoginClass {

    constructor() {

    }

    successAuth = (dataArgs)=> { //named arow function

    }

}

1
最好在要发布的代码中添加一个解释作为答案,以帮助访问者理解为什么这是一个很好的答案。
abarisone

这做什么的任择议定书表示,他并不想这样做,并依靠这个建议是只在第2阶段,可能甚至不打算让ES2017(但我认为这是有可能使ES2018和transpilers很支持数月)。它还有效地重复了几个先前的答案,这是没有用的。
TJ Crowder '02

-2

这是ES6

是的,我认为您追求的是这样的事情:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"

我试过这个例子,它工作正常。有否正票的理由?这种用法会产生任何有害的副作用吗?或者我错过了任何重要的观点?您在澄清我的疑问方面的帮助将不胜感激。
FullMoon

@GaneshAdapa:我希望投票赞成,因为这暗示着OP确实做了他/她不想做的事情,而没有提供任何进一步的信息。
TJ Crowder

-2

您可以跳过功能部分和箭头部分以创建功能。例:

 class YourClassNameHere{

   constructor(age) {
     this.age = age;
   }

   foo() {
     return "This is a function with name Foo";
   }

   bar() {
     return "This is a function with name bar";
   }

 }

let myVar = new YourClassNameHere(50);
myVar.foo();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.