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