Javascript 为什么使用命名函数表达式?

Javascript 为什么使用命名函数表达式?,javascript,function,anonymous-function,function-expression,Javascript,Function,Anonymous Function,Function Expression,在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()调用。我真的不明白为什么/什么时候应该使用匿名函数,什么时候应该使用命名函数表达式。它们之间有什么区别?

如果您希望能够引用所讨论的函数而不必依赖不推荐的功能,例如
参数,那么使用命名函数表达式会更好。被调用方
在匿名函数表达式的情况下,函数是匿名的-确切地说,它没有名称。分配给它的变量有名称,但函数没有名称。(更新:这在ES5中是正确的。从ES2015开始[aka ES6],通常使用匿名表达式创建的函数会获得真实名称[但不是自动标识符],请继续阅读…)

名字很有用。可以在堆栈跟踪、调用堆栈、断点列表等中看到名称。名称是一件好事™.

(您过去必须小心旧版IE[IE8及以下版本]中的命名函数表达式,因为它们在两个完全不同的时间错误地创建了两个完全独立的函数对象[在我的博客文章中有更多内容]。如果您需要支持IE8[!!],最好使用匿名函数表达式或函数声明,但避免使用命名函数表达式。)

命名函数表达式的一个关键点是,它在函数体中为函数创建一个具有该名称的范围内标识符:

var x=函数示例(){
console.log(示例类型);/“函数”
};
x();

console.log(示例类型);//“undefined”(未定义)命名函数如果需要引用自身(例如递归调用),则非常有用。事实上,如果将一个文本函数表达式作为参数直接传递给另一个函数,则该函数表达式不能在ES5严格模式下直接引用自身,除非它已命名

例如,考虑这个代码:

setTimeout(function sayMoo() {
    alert('MOO');
    setTimeout(sayMoo, 1000);
}, 1000);
如果传递给
setTimeout
的函数表达式是匿名的,那么就不可能如此干净地编写此代码;我们需要在调用
setTimeout
之前将其分配给一个变量。使用命名函数表达式,这种方法稍微短一点,更简洁

历史上,即使使用匿名函数表达式,也可以通过利用

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

避免使用
参数.callee()
,方法是为函数表达式指定名称,或者在函数必须调用自身的地方使用函数声明


(emphasis mine)

如果将函数指定为函数表达式,则可以为其命名

它将仅在功能内部可用(IE8-除外)

此名称用于可靠的递归函数调用,即使它被写入另一个变量

此外,NFE(命名函数表达式)名称可以用
Object.defineProperty(…)
方法覆盖,如下所示:

var test = function sayHi(name) {
  Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
  alert( test.name ); // foo
};

test();

注意:使用函数声明无法完成此操作。此“特殊”内部函数名仅在函数表达式语法中指定。

您应该始终使用命名的函数表达式,这就是为什么:

  • 当需要递归时,可以使用该函数的名称

  • 匿名函数在调试时没有帮助,因为您看不到导致问题的函数的名称

  • 当你不命名一个函数时,以后很难理解它在做什么。给它起个名字让它更容易理解

  • var foo=功能条(){
    //一些代码。。。
    };
    foo();
    bar();//错误!
    

    例如,在这里,因为名称栏是在函数表达式中使用的,所以它不会在外部范围中声明。对于命名函数表达式,函数表达式的名称包含在它自己的范围内。

    值得注意的是,至少有两个地方使用NFE仍然具有具体的优势:首先,对于打算通过
    new
    操作符用作构造函数的函数(给出所有这些函数的名称使得
    .constructor
    属性在调试过程中更有用,可以确定某个对象是什么样的实例),也可以用于直接传递到函数中的函数文本,而不必首先分配给属性或变量(例如
    setTimeout(函数(){/*do stuff*/});
    )。即使Chrome也会将它们显示为
    (匿名函数)
    ,除非您通过命名它们来帮助它。@MarkAmery:“这仍然是真的吗?我…尝试按CTRL-F键查找这些规则,但找不到它们”哦,是的。-“它散布在整个规范中,而不是在一个地方定义一组规则,只需搜索“setFunctionName”。我在上面添加了一小部分链接,但它目前出现在约29个不同的地方。如果您的
    setTimeout
    示例没有从为
    setTimeout
    声明的正式参数中获取名称(如果它有名称的话),我只会稍微感到惊讶。:-)是的,如果你知道你不会处理那些把它们弄得乱七八糟的旧浏览器,NFE肯定很有用。这更多的是一个评论,而不是一个答案。也许详细阐述会有好处
    var f = function sayHi(name) {
      alert( sayHi ); // Inside the function you can see the function code
    };
    
    alert( sayHi ); // (Error: undefined variable 'sayHi')
    
    var test = function sayHi(name) {
      Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
      alert( test.name ); // foo
    };
    
    test();