JavaScript中具有相同名称的两个函数 - 这如何工作?

时间:2011-02-28 12:57:25

标签: javascript function closures strict-mode

据我所知,function foo() { aaa(); }在JavaScript中只是var foo = function(){ aaa() }。因此,添加function foo() { bbb(); }应该覆盖foo变量,或者忽略第二个定义 - 这不是重点。关键是应该有一个变量foo

因此,在此示例中,{strong} }变量 > > > 我通过尝试将它们包装到另一个闭包(mevar中来到这个例子,但我很惊讶它没有必要:

me

演示:http://jsfiddle.net/W5dqy/5/

3 个答案:

答案 0 :(得分:33)

  

AFAIK函数foo(){aaa();在JavaScript中只是var foo = function(){aaa()}。

这实际上是不正确的。 JavaScript有两个不同但相关的东西:Function 声明和函数表达式。它们在解析周期的不同时间发生,并且具有不同的效果。

这是一个函数声明

function foo() {
    // ...
}

在执行任何分步代码之前,在进入封闭范围时处理函数声明。

这是一个函数表达式(具体来说,是一个匿名的):

var foo = function() {
    // ...
};

函数表达式作为逐步代码的一部分进行处理,在它们出现的位置处(就像任何其他表达式一样)。

您引用的代码使用命名函数表达式,如下所示:

var x = function foo() {
    // ...
};

(在你的情况下,它在一个对象文字中,所以它位于:的右侧而不是=,但它仍然是一个命名的函数表达式。)

这是完全有效的,忽略了实现错误(稍后更多)。它创建一个名为foo的函数,foo放入封闭范围,然后将该函数分配给x变量(全部在逐步代码中遇到表达式时会发生这种情况。当我说它没有将foo放在封闭范围内时,我的意思是:

var x = function foo() {
    alert(typeof foo); // alerts "function" (in compliant implementations)
};
alert(typeof foo);     // alerts "undefined" (in compliant implementations)

请注意,这与函数声明的工作方式有何不同(函数的名称 添加到封闭范围内)。

命名函数表达式适用于兼容的实现。从历史上看,实现中存在错误(早期的Safari,IE8和更早版本)。现代实现使它们正确,包括IE9及更高版本。 (此处更多:Double take和此处:Named function expressions demystified。)

  

因此,在这个示例中,me变量不能从方法内部进行核心解析

实际上,它应该是。函数的真实名称(function和左括号之间的符号)始终在函数内(函数来自声明或命名函数表达式)。

注意:以下是2011年编写的。随着JavaScript的进步,我不再觉得有必要做下面的事情,除非我知道我将要处理IE8 (这些日子非常罕见)。

由于实现错误,我曾经避免使用命名函数表达式。你可以在你的例子中删除me名称,但I prefer named functions,以及它的价值,这就是我以前编写你的对象的方式:

var foo = (function(){
    var publicSymbols = {};

    publicSymbols.bar1 = bar1_me;
    function bar1_me() {
        var index = 1;
        alert(bar1_me);
    }

    publicSymbols.bar2 = bar2_me;
    function bar2_me() {
        var index = 2;
        alert(bar2_me);
    }

    return publicSymbols;
})();

(除了我可能使用比publicSymbols更短的名字。)

以下是处理方式:

  1. 在逐步代码中遇到var foo = ...行时会创建一个匿名封闭函数,然后调用它(因为我在最后有())。< / LI>
  2. 在进入由该匿名函数创建的执行上下文时,处理bar1_mebar2_me函数声明,并将这些符号添加到该匿名函数内的作用域(从技术上讲,添加到变量对象,用于执行上下文)。
  3. publicSymbols符号将添加到匿名函数内的作用域中。 (更多:Poor misunderstood var
  4. 分步代码首先将{}分配给publicSymbols
  5. 分步代码继续publicSymbols.bar1 = bar1_me;publicSymbols.bar2 = bar2_me;,最后return publicSymbols;
  6. 匿名函数的结果已分配给foo
  7. 但是,现在,除非我正在编写代码,否则我需要支持IE8(遗憾的是,我在2015年11月写这篇文章时仍然有significant global market share,但很高兴这个分数急剧下降),我不知道担心它。所有现代JavaScript引擎都能理解它们。

    您也可以这样写:

    var foo = (function(){
        return {
            bar1: bar1_me,
            bar2: bar2_me
        };
    
        function bar1_me() {
            var index = 1;
            alert(bar1_me);
        }
    
        function bar2_me() {
            var index = 2;
            alert(bar2_me);
        }
    })();
    

    ...因为这些是函数声明,因此被悬挂。我通常不这样做,因为我发现如果我做声明并且相互旁边的属性分配(或者,如果不是为IE8编写,则在同一行上),对大型结构进行维护更容易

答案 1 :(得分:3)

两个me查找只能在函数表达式>

中可见/可用。

事实上,这两个是命名函数表达式,而ECMAscript规范告诉我们,表达式的名称不会暴露给这样的Variable object


嗯,我试着用几句话来说,但是在试图找到正确的词时,这最终会导致ECMAscript行为的深度链接。因此,function expression 存储在Variable / Activation Object中。 (会引出一个问题,那些人是谁......)。

简短:每次调用函数时,都会创建一个新的Context。有一种叫做“blackmagic”的家伙叫Activation object,它存放着一些东西。例如,

  • 函数的参数
  • [[Scope]]
  • var
  • 创建的所有变量

例如:

function foo(test, bar) {
    var hello = "world";

    function visible() {
    }

    (function ghost() {
    }());
}

foo的激活对象如下所示:

  • 参数:test,bar
  • 变量:hello(string),visible(function)
  • [[Scope]] :(可能的父函数 - 上下文),Global Object

ghost未存储在AO中!它只能在该功能本身的 名称下访问。虽然visible()函数声明(或函数声明),但它存储在AO中。这是因为,在运行时评估解析函数表达式时,会计算函数声明

答案 2 :(得分:1)

这里发生的事情是function()有许多不同的含义和用途。

当我说

bar1 : function me() {
}

那就是100%相当于

bar1 : function() {
}

即。使用函数分配变量bar1时,名称无关紧要。在内部,me已分配,但只要保留了函数定义(当您指定bar2时),me将再次创建为存储在{的函数定义的局部变量{1}}。