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

Han*_*Sun 9 javascript ecmascript-6

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

let a = (x) => x+1
Run Code Online (Sandbox Code Playgroud)

它将被转化为:

"use strict";

var a = function a(x) {
  return x + 1;
};
Run Code Online (Sandbox Code Playgroud)

var a = function a(x)看起来有点令我困惑,因为我理解其中任何一个var a = function(x)function a(x)足够.

有没有人有关于何时以及为什么需要将命名函数分配给变量的想法?

Max*_*ace 6

这里有两个不同的问题:

  1. 定义或表达函数的不同方式之间有什么区别?
  2. 为什么会这样let a = (x) => x + 1变换?

为了回答(2)我们需要理解(1) - 已经在SO和其他地方广泛讨论过.让我们来看看你提到的三个选择:

功能声明:

function a(x) { ... }
Run Code Online (Sandbox Code Playgroud)

从语法上讲,这些必须始终function(引用)开头.它们在分析时提升并在本地范围内创建命名函数.

(匿名)函数表达式:

var a = function (x) { ... }
Run Code Online (Sandbox Code Playgroud)

var a它本身将在解析时提升,但undefined直到此行在运行时执行.

命名函数表达式:

var a = function a(x) { ... }
Run Code Online (Sandbox Code Playgroud)

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

最大的区别在于函数声明函数表达式.通过声明,您可以:

a(1);
function a(x) { return x + 1; }
Run Code Online (Sandbox Code Playgroud)

虽然尝试使用函数表达式(命名匿名)会导致错误.

它应该是直观清楚为什么let a = (x) => x + 1应该被transpiled到函数声明.我们将箭头函数分配(x) => x + 1给一个块作用域的变量let,所以我们应该期望a直到在运行时执行该行之后才定义它.

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

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

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

箭头功能始终是匿名的

这个答案为什么使用命名函数表达式?说:

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

其他参考:

我发现掌握这个问题的最佳方法是使用Babel REPL.


Aln*_*tak 4

如果你写:

function a(x) { }
Run Code Online (Sandbox Code Playgroud)

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

然而,当你写:

var a = function a(x) { }
Run Code Online (Sandbox Code Playgroud)

then在实际执行该行之前,在封闭范围内var a不会有定义的值。

但是,该函数内,将存在一个不同的 a函数作为对函数本身的本地范围引用。

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