为何使用命名函数表达式?

Afs*_*ani 90 javascript function anonymous-function function-expression

我们有两种不同的方式在JavaScript中进行函数表达式:

命名函数表达式(NFE):

var boo = function boo () {
  alert(1);
};
Run Code Online (Sandbox Code Playgroud)

匿名函数表达式:

var boo = function () {
  alert(1);
};
Run Code Online (Sandbox Code Playgroud)

并且可以调用它们boo();.我真的不明白为什么/什么时候我应该使用匿名函数,何时我应该使用命名函数表达式.他们之间有什么区别?

T.J*_*der 82

在匿名函数表达式的情况下,函数是匿名的  - 字面上,它没有名称.您分配给它的变量有一个名称,但该函数没有.(更新:通过ES5确实如此.从ES2015 [aka ES6]开始,通常使用匿名表达式创建的函数会获得真正的名称,请继续阅读...)

名称很有用.名称可以在堆栈跟踪,调用堆栈,断点列表等中看到.名称是Good Thing™.

你必须要注意旧版IE(IE8及以下版本)中的命名函数表达式,因为IE在两个完全不同的时间错误地创建了两个完全独立的函数对象(更多在我的博客文章Double take中).如果你需要支持IE8,最好坚持使用匿名函数表达式或函数声明,但要避免使用命名函数表达式.

但是,从ES2015开始,许多"匿名"函数表达式创建了带有名称的函数,并且这种情况早于各种现代JavaScript引擎在从上下文推断名称方面非常聪明.在ES2015中,您的匿名函数表达式会生成一个带有名称的函数boo.这遍布整个规范,而不是在一个地方用一堆规则定义:搜索"SetFunctionName"的出现,目前在:

短版本基本上是匿名函数表达式出现在赋值或初始化等右侧的任何时候,如:

var x = function example() {
    console.log(typeof example); // "function"
};
x();
console.log(typeof example);     // "undefined"
Run Code Online (Sandbox Code Playgroud)

(或者它可以是varboo不是boo),或

var obj = {
    x: function() {
       console.log(typeof x);   // "undefined"
       console.log(obj.x.name); // "x"
    },
    y: function y() {
       console.log(typeof y);   // "function"
       console.log(obj.y.name); // "y"
    }
};
obj.x();
obj.y();
Run Code Online (Sandbox Code Playgroud)

要么

var boo = function() { /*...*/ };
Run Code Online (Sandbox Code Playgroud)

(最后两个实际上是相同的),结果函数将有一个名称(let在示例中).

有一个重要的,有意的例外:分配给现有对象的属性:

var obj = {
    boo: function() { /*...*/ }
};
Run Code Online (Sandbox Code Playgroud)

这是因为当新功能正在进行添加过程时引发的信息泄漏问题; 我在这里回答另一个问题的细节.

  • @MarkAmery:*"这仍然是真的吗?我...尝试按CTRL-F这些规则而无法找到它们"***哦是的.**:-)它遍布整个规范而不是在一个地方定义一组规则,只搜索"setFunctionName".我在上面添加了一小部分链接,但它现在显示在~29个不同的地方.如果你的`setTimeout`示例没有从为`setTimeout`声明的正式参数中获取名称,如果它有一个,我只会感到有些惊讶.:-)但是,如果你知道你不会处理那些制作哈希值的旧浏览器,NFE肯定是有用的. (4认同)

Mar*_*ery 22

如果命名函数需要引用它们(例如,用于递归调用),则它们很有用.实际上,如果您将文字函数表达式作为参数直接传递给另一个函数,那么该函数表达式不能在ES5严格模式下直接引用自身,除非它被命名.

例如,考虑以下代码:

setTimeout(function sayMoo() {
    alert('MOO');
    setTimeout(sayMoo, 1000);
}, 1000);
Run Code Online (Sandbox Code Playgroud)

如果传递给的函数表达式setTimeout是匿名的,那么就不可能干净地编写这段代码.我们需要在setTimeout调用之前将其分配给变量.这样,使用命名函数表达式,稍微短一些.

在历史上可以使用匿名函数表达式编写这样的代码,通过利用arguments.callee...

setTimeout(function () {
    alert('MOO');
    setTimeout(arguments.callee, 1000);
}, 1000);
Run Code Online (Sandbox Code Playgroud)

...但arguments.callee已被弃用,并且在ES5严格模式下是完全禁止的.因此,MDN建议:

arguments.callee()通过为函数表达式赋予名称或使用函数必须调用自身的函数声明来避免使用.

(强调我的)