从构造函数返回命名函数

4
我从未见过这种语法的使用方式,但它在Angular源代码中随处可见。
function something() {
    // do stuff...
    return function foo() {
        // do stuff..
    };
}

// later
var x = something();

据我所知,foo() 被用作闭包函数,但为什么这个函数有一个名字呢?我以为给闭包函数命名是无效的语法,但在浏览器中似乎可以正常运行。
上述代码和下面的代码有什么不同吗?如果有,是什么?
function something() {
    // do stuff...
    return function() {
        // do stuff..
    };
}

// later
var x = something();

好的,既然你没有执行这个函数,在下面的例子中,x将是一个函数定义,不是吗?它可能是一种维护处理数据作用域的用法,因为某些变量仍然在作用域内,只是移动到其他地方了?如果有可能,我也想得到启示。:-D - Fallenreaper
2个回答

5
这与闭包中包含的函数无关。
真正的区别在于函数声明:
function myFunc() { /* ... */ }

和一个函数表达式

var myFunc = function() { /* ... */ };

或者

var myObj = {
    myFunc: function() { /* ... */ },
    // ...
}

在这些情况下,函数表达式是匿名的。你所讨论的是一个命名函数表达式:
var myFunc = function privateName() { /* ... */ };

(请注意,内部的privateName不必与myFunc匹配。)
相比匿名函数,它们有两个明显的优点和一个缺点。缺点是:
  • IE至少在IE8中,会为你想要创建的每个这样的函数创建两个副本。
这些优点是:
  • 名称出现在调试器中,使得查找更加容易。

  • 名称仅在函数内部可用,而不污染全局命名空间,并且无需依赖于已弃用的特性,如argument.callee,因此您可以将其用于递归或其他内部用途。

Kangax对它们写了一篇权威的文章:命名函数表达式的解析

IE的“劣势”只有在由此引起名称冲突时才是问题。如果你期望使用IE,那就充分利用它们,否则就不要考虑它们的存在! - Paul S.
“函数声明”仍然是一个有效的表达式。它的值是该函数本身。你想要强调的是命名匿名函数表达式之间的区别。 - Aryeh Leib Taurog
具名函数与当前对象的原型有什么关系吗? - Reactgular
@PaulS:如果你读了kangax的文章,你可能会认为即使在仅限IE的情况下,这也更像是一个缺点。 - Scott Sauyet
@AryehLeibTaurog:你说得对。这个前言太冗长了。其实没有必要深入探讨函数声明和函数表达式之间的区别,真正的问题只是命名函数表达式和匿名函数表达式之间的区别。 - Scott Sauyet
@MathewFoscarini:不,这些是无关概念。 - Scott Sauyet

3
一个命名函数表达式是一个很棒的东西,它允许您在同一函数内轻松缓存和调用(递归调用)。

例如,假设您想知道一个函数被调用了多少次:

var anonNamedFn = function fn() {
    fn.cache = fn.cache || {
        callCount: 0
    };

    fn.cache.callCount += 1;
};

使用递归的另一个示例:

var anonNamedFn = function fn(n) {
    if (n > 0) {
        return fn(n-1);
    }
};

它允许消除this上下文的歧义。

更新

它有助于调试,因为如果您选择一个明智的函数名称,它会显示函数的名称。

但要注意,只有真正的浏览器支持这个特性(IE9+适用于IE)。

编辑 匿名命名函数 => 命名函数表达式 Paul S.


但是...如果它有一个名字,它就不是匿名的了。 - Paul S.
是的,这就是我喜欢彩虹的原因。 - axelduch
我认为你要找的术语是“命名函数表达式”(使用“function”作为运算符)。 - Paul S.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接