在JavaScript中,我们有两种不同的方式来进行函数表达式:
命名函数表达式(NFE):
var boo = function boo () {
alert(1);
};
匿名函数表达式:
var boo = function () {
alert(1);
};
这两者都可以使用boo();
调用。我真的看不出为什么/何时应该使用匿名函数和何时应该使用具名函数表达式。它们之间有什么区别?
在JavaScript中,我们有两种不同的方式来进行函数表达式:
命名函数表达式(NFE):
var boo = function boo () {
alert(1);
};
匿名函数表达式:
var boo = function () {
alert(1);
};
这两者都可以使用boo();
调用。我真的看不出为什么/何时应该使用匿名函数和何时应该使用具名函数表达式。它们之间有什么区别?
var x = function example() {
console.log(typeof example); // "function"
};
x();
console.log(typeof example); // "undefined"
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();
var boo = function() { /*...*/ };
(或者它可以是let
或const
而不是var
),或者
var obj = {
boo: function() { /*...*/ }
};
或者
doSomething({
boo: function() { /*...*/ }
});
(最后两个其实是同一件事),得到的函数将有一个名称(例如示例中的boo
)。
有一个重要且故意的例外情况:对现有对象上的属性进行赋值:
obj.boo = function() { /*...*/ }; // <== Does not get a name
new
运算符用作构造函数的函数(为所有这些函数命名使.constructor
属性在调试期间更有用,以确定某个对象是实例的原因),其次是直接传递给函数的函数文字,而不是首先分配给属性或变量(例如setTimeout(function () {/*do stuff*/});
)。即使是Chrome也将它们显示为“(匿名函数)”,除非您通过命名来帮助它。 - Mark AmerysetTimeout
有形式参数声明,那么我可能只会感到稍微惊讶您的示例没有捕获名称。:-) 但是,如果您知道不会涉及将其搞砸的旧浏览器,则NFE肯定非常有用。 - T.J. Crowder命名函数有用处,如果它们需要引用自身(例如递归调用)。确实,如果您将文字函数表达式直接作为参数传递给另一个函数,则在 ES5 严格模式下,该函数表达式 无法 直接引用自身,除非它被命名。
例如,考虑以下代码:
setTimeout(function sayMoo() {
alert('MOO');
setTimeout(sayMoo, 1000);
}, 1000);
如果传递给 setTimeout
的函数表达式是匿名的,那么要以这种干净的方式编写此代码几乎是不可能的。我们需要在调用 setTimeout
之前将其分配给一个变量。使用命名函数表达式的方式则更加简短和整洁。arguments.callee
...setTimeout(function () {
alert('MOO');
setTimeout(arguments.callee, 1000);
}, 1000);
... 但是 arguments.callee
已经被弃用,并且在 ES5 严格模式下完全禁止使用。因此,MDN 建议:
通过为函数表达式命名或者在必须调用自身的地方使用函数声明来避免使用
arguments.callee()
。
(我强调)
你应该始终使用命名函数表达式,原因如下:
当你需要递归时,可以使用该函数的名称。
匿名函数在调试时无法帮助你查看导致问题的函数名称。
如果不给函数命名,后来很难理解它在做什么。给它一个名称会使它更容易理解。
var foo = function bar() {
//some code...
};
foo();
bar(); // Error!
var f = function sayHi(name) {
alert( sayHi ); // Inside the function you can see the function code
};
alert( sayHi ); // (Error: undefined variable 'sayHi')
这个名字是为了可靠的递归函数调用而设计的,即使它被写到另一个变量中。
此外,NFE(命名函数表达式)的名称可以通过Object.defineProperty(...)
方法进行覆盖,如下所示:
var test = function sayHi(name) {
Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
alert( test.name ); // foo
};
test();
当您想要引用函数本身而不依赖于已弃用的特性,如arguments.callee
时,使用命名函数表达式更好。