命名回调函数表达式是什么?

3

以下是示例:

setTimeout(function name(){console.log(1)}, 1000)  (callback)

name()(回调函数)被认为是函数声明还是命名函数表达式?

我知道函数声明可以被提升,而函数表达式则不行,但因为它在参数的上下文中,你如何确定是否可以提升它?

我不认为这是一个关于函数声明和函数表达式之间区别的重复问题。

我询问的是特定用例,即命名回调函数是函数声明还是函数表达式。我不认为这个用例在那个问题的答案中得到了涵盖。


顺便说一下,它不是匿名的。 - Igor Soloydenko
2个回答

5
一般而言,如果函数定义在表达式位置,则为函数表达式。 何时才算是“表达式位置”?当你无法在那里放置语句时。 参数是表达式(你不能将if语句作为参数传递),因此function name() {...}在你的示例中是一个表达式。 并且因为它是一个函数表达式,你可以省略名称。
现在你可能会想到以下问题:假设你知道5 + 5是一个表达式,你可以像这样把5 + 5放在一行中。
5 + 5;

看起来我们可以在任何地方放置表达式。那么,鉴于此,JavaScript如何决定

function name() {
  //...
}

无论是函数声明还是函数表达式?
要回答这个问题,我们需要看一下代码的整体结构。在顶层,你只有一个语句列表:
<statement1>
<statement2>
<statement3>
...

有各种类型的语句, if 语句就是其中之一。还有一个概念是 ExpressionStatement。正如其名称所示,它是由单个表达式组成的语句。这就是允许您在语句列表中放置 5 + 5; 的规则。

但是,这个解析规则是受限制的。它不能以关键字 function(等等)开头。因此,当 JavaScript 遇到上述函数定义时,它不会将其解析为函数表达式,因为它无法将其解析为 ExpressionStatement。唯一的选择是将其解析为一个函数 声明


如果你对JavaScript程序的结构感到好奇,你可以使用https://astexplorer.net(完全透明:我建造了这个网站)来查看流行的JavaScript解析器如何剖析程序。
例如,
setTimeout(function name(){console.log(1)}, 1000)

被解析为

enter image description here

acorn提供。

(你应该查看实时版本,因为将鼠标悬停在AST上会突出显示相应的源文本,这使得理解更加容易)


哇,这真的很令人印象深刻。我想,如果我需要确定一个函数是表达式还是声明,我知道该去哪里查找 :) - the12
只是想要百分之百确定,因为我在睡觉时就在思考这个问题了。一个函数是表达式还是声明取决于它相对于代码的位置,而不是语法。通过代码中的位置,我指的是函数前面和后面的代码。例如:回调是一个表达式,由于其在代码中的位置(作为参数),它只能是一个表达式。 - the12
是的,如果我理解你的意思正确的话,那么你是正确的 ;) - Felix Kling

1

是的,它是一个表达式。并且可以通过其名称从函数内部进行调用。

试一下这个:

setTimeout(function name(){
  console.log(1);
  console.log(name);
}, 1000)

不过要使用停止条件来调用它,否则你会耗尽堆栈。递归可能会带来麻烦...


只是想知道你是如何知道它是一个表达式的,因为函数声明也可以通过其名称进行调用。 - the12
1
在编程中,“表达式”是一组值和函数的组合,这些值和函数结合起来创建一个新值,而“语句”只是一个独立的执行单元,不返回任何内容。函数声明实际上会返回新函数。 - Sidney
1
@Sidney:它不会返回一个函数。函数对象是作为副作用创建的。您可以在语言规范中看到,评估函数声明不会返回任何内容。 - Felix Kling
1
@FelixKling 我不确定我理解这个区别。function name() {} 返回了一些东西,对吧?它不返回一个函数吗?当然,实际上它将是一个对象,因为在JavaScript中函数是对象。该函数也会作为副作用被提升。 - Sidney
1
@Sidney:在这个例子中,它返回一个函数表达式,因此它被评估为一个函数。但是,评估函数声明不会返回任何内容。我区分函数声明和函数表达式。 - Felix Kling

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