简体   繁体   English

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

[英]In Javascript, when is it necessary to assign a named function to a variable?

In the online REPL of Babel JS ( http://babeljs.io/repl/ ), when I type in :在 Babel JS ( http://babeljs.io/repl/ ) 的在线 REPL 中,当我输入:

let a = (x) => x+1

It will be transpiled to:它将被转译为:

"use strict";

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

Here the var a = function a(x) looks a bit confusing to me, because either var a = function(x) or function a(x) is enough as I understand.这里var a = function a(x)对我来说看起来有点混乱,因为我理解var a = function(x)function a(x)就足够了。

Does anyone have ideas about when and why it is necessary to assign a named function to a variable?有没有人知道何时以及为什么需要将命名函数分配给变量?

There are really two different questions here:这里真的有两个不同的问题:

  1. What are the differences between the different ways of defining or expressing functions?定义或表达函数的不同方式之间有什么区别?
  2. Why does let a = (x) => x + 1 get transpiled this way?为什么let a = (x) => x + 1以这种方式转译?

In order to answer (2) we need to understand (1)-- which has been extensively discussed on SO and elsewhere.为了回答(2),我们需要理解(1)——这已经在 SO 和其他地方进行了广泛的讨论。


Question 1问题 1

Let's go through the three alternatives you mentioned:让我们来看看你提到的三种选择:

Function declaration :函数声明

function a(x) { ... }

Syntactically, these must always begin with function ( reference ).从语法上讲,这些必须始终function ( reference ) 开头。 They are hoisted at parse time and create a named function in the local scope.它们在解析时被提升并在本地范围内创建一个命名函数。

(Anonymous) Function expression : (匿名)函数表达式

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

var a itself will be hoisted at parse time, but it will be undefined until this line is executed at runtime. var a本身将在解析时提升,但在运行时执行此行之前它将是undefined

Named Function expression :命名函数表达式

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

Though the syntax makes it looks like an assignment to a function declaration , this is actually just a function expression with a name.尽管语法使它看起来像对函数声明的赋值,但这实际上只是一个带有名称的函数表达式 I find this confusing, but that's the syntax.我觉得这很令人困惑,但这就是语法。

The big difference is between function declarations and function expressions .最大的区别在于函数声明函数表达式之间 With a declaration, you can do:通过声明,您可以执行以下操作:

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

though attempting this with a function expression ( named or anonymous ) will cause an error.尽管尝试使用函数表达式命名匿名)执行此操作会导致错误。


Question 2问题2

  1. Why does let a = (x) => x + 1 get transpiled this way?为什么让 a = (x) => x + 1 以这种方式转译?

We're assigning the arrow function (x) => x + 1 to a block-scoped variable with let , so we should expect that a is not defined until after this line has been executed at runtime.我们使用let将箭头函数(x) => x + 1分配给块范围的变量,因此我们应该期望在运行时执行此行之后才定义a This should be a function expression , not a function declaration .这应该是一个函数表达式,而不是一个函数声明

Last, why is let a = (x) => x + 1 transpiled to a named function expression rather than a anonymous function expression ?最后,为什么let a = (x) => x + 1转译为命名函数表达式而不是匿名函数表达式 What's the difference?有什么不同? As Alnitak and others have pointed out:正如 Alnitak 和其他人指出的那样:

  • Function names appear in debuggers, which can be helpful.函数名称出现在调试器中,这很有帮助。
  • The scope inside of a named function definition has a reference to the function itself.命名函数定义内的作用域具有对函数本身的引用。 This allows for recursion and accessing properties of the containing function.这允许递归和访问包含函数的属性。

So named function expressions have some nice properties that anonymous function expressions don't.因此,命名函数表达式具有匿名函数表达式所没有的一些很好的特性。 But there actually seems to be disagreement on what should happen here.但实际上似乎对这里应该发生的事情存在分歧。 According to MDN :根据MDN

Arrow functions are always anonymous箭头函数总是匿名的

whereas this answer to Why use named function expressions?这个为什么使用命名函数表达式的答案 says:说:

"[As of ES6] a lot of "anonymous" function expressions create functions with names, and this was predated by various modern JavaScript engines being quite smart about inferring names from context... This is strewn throughout the spec" “[从 ES6 开始] 许多“匿名”函数表达式创建带有名称的函数,而这早于各种现代 JavaScript 引擎非常聪明地从上下文推断名称......这散布在整个规范中”

Other references:其他参考:

I've found that the best way to get a handle on this is playing around with the Babel REPL .我发现解决这个问题的最好方法是使用Babel REPL

If you write:如果你写:

function a(x) { }

then the function is hoisted to the top of the enclosing scope, and a becomes available immediately at parse time within the entire scope.然后函数被提升到封闭作用域的顶部,并且a在整个作用域内在解析时立即可用。

However, when you write:但是,当你写:

var a = function a(x) { }

then var a will not have a defined value in the enclosing scope until this line is actually executed.那么在实际执行此行之前, var a在封闭范围内将没有定义的值

However, within that function, a different a will exist as a locally scoped reference to the function itself.但是,该函数中,不同的a将作为对函数本身的局部范围引用而存在。

By using the let a = function ... construct Babel is being more consistent with the latter form, ensuring that a is assigned at run time to a (named) function expression instead of using a parse time function declaration .通过使用let a = function ...构建巴别正在与后者形式更一致,以确保a在运行时分配给一个(命名)的函数表达式,而不是使用解析时间函数声明

It appears that this is according to standard ( 12.14.4 ):看来这是根据标准( 12.14.4 ):

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

1.If LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral, then 1.如果 LeftHandSideExpression 既不是 ObjectLiteral 也不是 ArrayLiteral,则
a.一种。 Let lref be the result of evaluating LeftHandSideExpression.令 lref 为评估 LeftHandSideExpression 的结果。
b.ReturnIfAbrupt(lref). ReturnIfAbrupt(lref)。
c. C。 Let rref be the result of evaluating AssignmentExpression.让 rref 是对 AssignmentExpression 求值的结果。
d. d. Let rval be GetValue(rref).令 rval 为 GetValue(rref)。
e. e. If IsAnonymousFunctionDefinition(AssignmentExpression) and IsIdentifierRef of LeftHandSideExpression are both true, then如果 LeftHandSideExpression 的 IsAnonymousFunctionDefinition(AssignmentExpression) 和 IsIdentifierRef 都为真,则
I. Let hasNameProperty be HasOwnProperty(rval, "name"). I. 让 hasNameProperty 为 HasOwnProperty(rval, "name")。
II.二、 ReturnIfAbrupt(hasNameProperty). ReturnIfAbrupt(hasNameProperty)。
III.三、 If hasNameProperty is false, perform SetFunctionName(rval, GetReferencedName(lref)) .如果 hasNameProperty 为 false,则执行SetFunctionName (rval, GetReferencedName(lref))

So, whenever an assignment of an unnamed function expressions to a named identifier is evaluated, the function name should be set to the identifer name.因此,无论何时计算未命名函数表达式命名标识符的赋值,函数名称都应设置为标识符名称。

Babel follows this process, and generates a compatible ES5 implementation. Babel 遵循这个过程,并生成一个兼容的 ES5 实现。
Chrome (v46.0.2490.71, V8 engine...), doesn't follow this process, and name equals '' in that cases. Chrome(v46.0.2490.71,V8 引擎...)不遵循此过程,在这种情况下, name等于''


As for the question itself...至于问题本身...

In Javascript, when is it necessary to assign a named function to a variable?在 Javascript 中,什么时候需要将命名函数分配给变量?

The answer is never .答案是永远 It's up to the developer to decide if / when to use a named function.是否/何时使用命名函数由开发人员决定。 The decision boils down to specific need for a name (such as when "stringifying" a function), or debug needs (better stack traces...).该决定归结为对名称的特定需求(例如“字符串化”函数时)或调试需求(更好的堆栈跟踪...)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM