代码之家  ›  专栏  ›  技术社区  ›  Afshin Mehrabani Mohd Jafar

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

  •  102
  • Afshin Mehrabani Mohd Jafar  · 技术社区  · 11 年前

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

    命名函数表达式(NFE) :

    var boo = function boo () {
      alert(1);
    };
    

    匿名函数表达式 :

    var boo = function () {
      alert(1);
    };
    

    他们两个都可以用 boo(); 。我真的不明白为什么/什么时候应该使用匿名函数,什么时候应该用命名函数表达式。他们之间有什么区别?

    5 回复  |  直到 7 年前
        1
  •  96
  •   T.J. Crowder    5 年前

    对于匿名函数表达式,函数为 匿名的 -从字面上看,它没有名字。指定给它的变量有一个名称,但函数没有。 (更新: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() { /*...*/ }
    });
    

    (最后两个真的是一样的) ,生成的函数将有一个名称( 喝倒采 ,在示例中)。

    有一个重要且有意的异常:为现有对象的属性赋值:

    obj.boo = function() { /*...*/ }; // <== Does not get a name
    

    这是因为在添加新功能的过程中,人们担心信息泄露;我对另一个问题的回答中的细节 here .

        2
  •  26
  •   Mark Amery Harley Holcombe    9 年前

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

    例如,考虑以下代码:

    setTimeout(function sayMoo() {
        alert('MOO');
        setTimeout(sayMoo, 1000);
    }, 1000);
    

    如果函数表达式传递给 setTimeout 是匿名的;我们需要在 设置超时 呼叫使用命名函数表达式,这种方式会稍微简短一些。

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

    setTimeout(function () {
        alert('MOO');
        setTimeout(arguments.callee, 1000);
    }, 1000);
    

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

    避免使用 arguments.callee() 由任一方 为函数表达式命名 或者在函数必须调用自身的情况下使用函数声明。

    (重点是我的)

        3
  •  4
  •   Mike Omeiri Kouider    5 年前

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

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

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

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

    var foo = function bar() {
        //some code...
    };
    
    foo();
    bar(); // Error!
    

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

        4
  •  3
  •   Roman    6 年前

    如果函数被指定为函数表达式,则可以为其指定一个名称。

    它将仅在函数内部可用(IE8-除外)。

    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();
    

    注意:对于函数声明,这是不可能做到的。此“特殊”内部函数名称仅在函数表达式语法中指定。

        5
  •  1
  •   Sudhir Bastakoti    11 年前

    当您希望能够引用有问题的函数而不必依赖于诸如 arguments.callee .