18

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

因此,在此示例中,不应从方法内部正确解析me变量,并且它不在 Explorer 8 中:-)。我通过尝试将它们包装到另一个闭包中来达到这个例子, ( )将是,但令我惊讶的是它没有必要:varme

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

    foo.bar1(); // Shows the first one
    foo.bar2(); // Shows the second one

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

4

3 回答 3

35

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

不完全的; 它们很相似,但也完全不同。JavaScript 有两个不同但相关的东西:函数声明(您的第一个示例)和函数表达式(您的第二个,然后将其分配给变量)。它们在解析周期的不同时间发生,并具有不同的效果。

这是一个函数声明

function foo() {
    // ...
}

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

这是一个函数表达式(特别是匿名的):

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

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

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

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

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

这是完全有效的,忽略实现错误(稍后会更多)。它创建一个名为 的函数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和这里​​:命名函数表达式揭秘。)

因此,在此示例中,me变量不应从方法内部正确解析

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

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

由于实现错误,我曾经避免使用命名函数表达式。您可以在您的示例中通过删除me名称来做到这一点,但我更喜欢命名函数,因此对于它的价值,这是我用来编写您的对象的方式:

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 = ...当分步代码中遇到该行时,会创建一个匿名封闭函数,然后调用它(因为我()在最后有 )。
  2. 进入由该匿名函数创建的执行上下文后,将处理bar1_mebar2_me函数声明,并将这些符号添加到该匿名函数内部的作用域中(从技术上讲,添加到执行上下文的变量对象中)。
  3. publicSymbols符号被添加到匿名函数内的范围内。(更多:可怜的误解var
  4. 分步代码从分配{}到开始publicSymbols
  5. 逐步代码继续使用publicSymbols.bar1 = bar1_me;and publicSymbols.bar2 = bar2_me;,最后return publicSymbols;
  6. 匿名函数的结果分配给foo.

不过,这些天来,除非我正在编写我知道需要支持 IE8 的代码(遗憾的是,当我在 2015 年 11 月写这篇文章时,它仍然拥有重要的全球市场份额,但令人高兴的是,该份额正在直线下降),我并不担心. 所有现代 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 编写,在同一行上),则更容易对大型结构进行维护.

于 2011-02-28T13:03:30.600 回答
3

两种me查找都仅在函数表达式中可见/可用

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


好吧,我试图只用几句话来表达,但是在尝试找到正确的词时,这最终导致了相当深的 ECMAscript 行为链。因此,function expression存储在/中。(会引出一个问题,那些人是谁......)。VariableActivation Object

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

  • 函数的参数
  • 范围]]
  • 由创建的任何变量var

例如:

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

    function visible() {
    }

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

的激活对象foo如下所示:

  • 参数:测试,酒吧
  • 变量:你好(字符串),可见(函数)
  • [[Scope]]:(可能的父函数上下文),全局对象

ghost不存储在AO中!它只能在函数本身内以该名称访问。Whilevisible()是一个函数声明(或函数语句),它存储在 AO 中。这是因为,在解析时评估函数声明,并在运行时评估函数表达式。

于 2011-02-28T13:05:43.823 回答
1

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

当我说

bar1 : function me() {
}

那么这100%相当于

bar1 : function() {
}

即,当您使用函数分配变量时,名称并不重要bar1。在内部,me被赋值,但只要函数定义离开(当你赋值时bar2),me就会再次创建为存储在bar2.

于 2011-02-28T13:06:14.307 回答