在Javascript中,何时需要为变量分配命名函数?

时间:2015-11-10 09:47:17

标签: javascript ecmascript-6

在Babel JS的在线REPL(http://babeljs.io/repl/)中,当我输入时:

let a = (x) => x+1

将被转化为:

"use strict";

var a = function a(x) {
  return x + 1;
};

此处var a = function a(x)对我来说有点让人困惑,因为我理解var a = function(x)function a(x)就足够了。

有没有人知道何时以及为什么有必要将命名函数分配给变量?

3 个答案:

答案 0 :(得分:6)

这里有两个不同的问题:

  1. 定义或表达函数的不同方式有何不同?
  2. 为什么let a = (x) => x + 1会以这种方式进行转换?
  3. 为了回答(2)我们需要理解(1) - 已在SO和其他地方广泛讨论过。让我们来看看你提到的三个选择:

    功能声明

    function a(x) { ... }
    

    从语法上讲,这些必须始终functionreference)开头。它们在分析时提升并在本地范围内创建一个命名函数。

    (匿名)函数表达式

    var a = function (x) { ... }
    

    var a本身将在解析时提升,但在运行时执行此行之前它将为undefined

    命名函数表达式

    var a = function a(x) { ... }
    

    虽然语法使它看起来像是对函数声明的赋值,但这实际上只是一个带有名称的函数表达式。我发现这令人困惑,但这就是语法。

    函数声明函数表达式之间存在很大差异。通过声明,您可以:

    a(1);
    function a(x) { return x + 1; }
    

    虽然尝试使用函数表达式名为匿名)会导致错误。

    应该直观地清楚为什么let a = (x) => x + 1应该被转换为函数声明。我们将箭头函数(x) => x + 1分配给带有let的块作用域变量,因此我们应该期望在运行时执行此行之后才定义a

    所以,我们还有一个问题:为什么let a = (x) => x + 1被转换为命名函数表达式而不是匿名函数表达式?有什么不同?正如Alnitak和其他人指出的那样:

    • 函数名称出现在调试器中,这可能会有所帮助。
    • 命名函数定义中的范围具有对函数本身的引用。这允许递归和访问包含函数的属性。

    所以命名函数表达式有一些很好的属性,匿名函数表达式没有。但实际上似乎对这里应该发生的事情存在分歧。根据{{​​3}}:

      

    箭头功能始终是匿名的

    MDNthis answer说:

      

    “[从ES6开始]很多”匿名“函数表达式创建了带有名称的函数,这是各种现代JavaScript引擎在从上下文推断名称时非常聪明的事情......这遍布整个规范”< / p>

    其他参考资料:

    我发现处理此问题的最佳方法是使用Javascript - Precedence in hoisting

答案 1 :(得分:4)

如果你写:

function a(x) { }

然后该函数被提升到封闭范围的顶部,并且a在整个范围内的解析时立即可用。

然而,当你写:

var a = function a(x) { }

然后var a在封闭范围内不会有定义的值,直到实际执行此行为止。

但是,函数中,不同的 a将作为函数本身的本地作用域引用存在。

使用let a = function ...构造Babel与后一种形式更加一致,确保在运行时将a分配给(命名)函数表达式,而不是使用解析时间函数声明

答案 2 :(得分:2)

这似乎符合标准(12.14.4):

  

AssignmentExpression [In,Yield]:LeftHandSideExpression [?Yield] = AssignmentExpression [?In,?Yield]

     

1.如果LeftHandSideExpression既不是ObjectLiteral也不是ArrayLiteral,那么
  一个。让lref成为评估LeftHandSideExpression的结果   湾ReturnIfAbrupt(LREF)。
  C。让rref成为评估AssignmentExpression的结果   d。设rval为GetValue(rref)   即如果LeftHandSideExpression的IsAnonymousFunctionDefinition(AssignmentExpression)和IsIdentifierRef都为true,那么
  I.让hasNameProperty为HasOwnProperty(rval,“name”)   II。 ReturnIfAbrupt(hasNameProperty)。
  III。如果hasNameProperty为false,则执行SetFunctionName(rval,GetReferencedName(lref))

因此,每当评估 未命名函数表达式 命名标识符 的评估时,该功能name应设置为标识名称。

Babel遵循此过程,并生成兼容的ES5实现 Chrome(v46.0.2490.71,V8引擎...)不遵循此流程,在这种情况下name等于''

至于问题本身......

  

在Javascript中,何时需要将命名函数分配给变量?

答案是从不。由开发人员决定是否/何时使用命名函数。决定归结为对名称的特定需求(例如“字符串化”函数时)或调试需求(更好的堆栈跟踪......)。