在匿名函数表达式的情况下,该函数是 anonymous - 从字面上看,它没有名称。您分配给它的变量有一个名称,但函数没有。 (更新:在 ES5 中确实如此。从 ES2015 [又名 ES6] 开始,使用匿名表达式创建的函数通常会得到一个真实的名称 [但不是自动标识符],请继续阅读......)
名字很有用。名称可以在堆栈跟踪、调用堆栈、断点列表等中看到。名称是一件好事™。
(您曾经不得不提防旧版本 IE [IE8 及以下] 中的命名函数表达式,因为它们错误地在两个完全不同的时间创建了两个完全独立的函数对象 [更多信息请参阅我的博客文章 Double take]。如果你需要支持 IE8 [!!],最好还是坚持匿名函数表达式或函数声明,但避免命名函数表达式。)
命名函数表达式的一个关键是它为函数体内的函数创建了一个具有该名称的范围内标识符:
var x = function example() {
console.log(typeof example); // "function"
};
x();
console.log(typeof example); // "undefined"
不过,从 ES2015 开始,许多“匿名”函数表达式创建带有名称的函数,而这早于各种现代 JavaScript 引擎,它们非常聪明地从上下文推断名称。在 ES2015 中,您的匿名函数表达式会生成一个名为 boo 的函数。但是,即使使用 ES2015+ 语义,也不会创建自动标识符:
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();
函数名称的分配是通过规范中各种操作中使用的SetFunctionName抽象操作完成的。
简短的版本基本上是任何时候匿名函数表达式出现在赋值或初始化之类的右侧,例如:
var boo = function() { /*...*/ };
(或者可以是let 或const 而不是var),或者
var obj = {
boo: function() { /*...*/ }
};
或
doSomething({
boo: function() { /*...*/ }
});
(最后两个实际上是同一件事),生成的函数将有一个名称(在示例中为boo)。
有一个重要且有意的例外:分配给现有对象的属性:
obj.boo = function() { /*...*/ }; // <== Does not get a name
这是因为在添加新功能的过程中出现了信息泄露问题;我对另一个问题here的回答中的详细信息。