(此答案已有 4 年之久(截至 2015 年 4 月),虽然它仍然正确,但我认为它需要更一般的解释 - 见下文)
原答案
想一想:
(function (x) {
// ...
})(y);
作为:
function functionName(x) {
// ...
}
functionName(y);
但不需要给它一个名字(比如functionName)。
所以这个:
(function(global) {
// ...
})(typeof window === 'undefined' ? this : window);
真的只是:
function functionName(global) {
// ...
}
functionName(typeof window === 'undefined' ? this : window);
它是一个有一个参数的函数(在函数中称为global),它以typeof window === 'undefined' ? this : window 调用,含义相同:
function functionName(global) {
// ...
}
if (typeof window === 'undefined') {
functionName(this);
} else {
functionName(window);
}
但使用更短的符号(并且没有命名函数)。
更一般的解释
我在 4 年前写了这个答案,我认为是时候对这里涉及的概念添加一些更一般的解释了。
如上所述,这是:
(function (x) {
// ...
})(y);
是这个的匿名版本:
function functionName(x) {
// ...
}
functionName(y);
其中(如果您只调用一次)通常(见下文例外)也与此相同:
function functionName() {
var x = y;
// ...
}
functionName();
回到匿名立即调用的函数,这个:
(function (x) {
// ...
})(y);
和这个是一样的:
(function () {
var x = y;
// ...
})();
这对大多数人来说可能具有更明显的含义。 (这里立即调用的函数没有参数,只是为了给我们一个变量和其他嵌套函数的隔离范围,这样我们就不会污染外部或全局范围——这也是在首先立即调用匿名函数。)
在括号内
顺便说一句:
(function () {
// ...
})();
和这个是一样的:
(function () {
// ...
}());
由于语言的歧义,函数周围的括号是必需的,但它们可能包括实际调用函数的(),也可能不包括——尽管有些人认为这里的第二种形式看起来更清晰。请参阅this explanation by Douglas Crockford 了解更多详细信息以及为什么他认为第一个版本看起来像“狗球”。
例外情况
之前我说过这个:
(function (x) {
// ...
}(y));
和这个是一样的:
(function () {
var x = y;
// ...
}());
这在大多数情况下对于一个参数是正确的(它不会在外部范围内屏蔽同名变量同时取决于它的值)并且通常对于多个参数是正确的(如果它们也不相互依赖)。我希望这些例子会更清楚。
当我们有这个代码时:
(function (x) {
// ...
}(x + 1));
那么我们不能把它翻译成:
(function () {
var x = x + 1;
// ...
}());
因为在第一个版本中,我们将 1 添加到 outer x 并将结果绑定到 inner x 并且一旦我们进入函数内部我们只有内部的x 可以使用(甚至新的let 语句也无济于事)。
另一个例子:
(function (x, outer_x) {
// ...
}(1, x));
这里你可以将外部作用域的old_x设置为x的值,将内部作用域的x设置为一个新的值1,你不必担心顺序。但如果你这样做了:
总结
如您所见,有些情况您不能简单地翻译:
(function (x) {
// ...
}(y));
进入:
(function () {
var x = y;
// ...
}());
但我认为,如果它可以翻译成第二种形式,那么它应该是为了可读性。特别是对于较大的函数,您必须滚动到函数末尾才能知道函数顶部使用的变量的含义是不方便的。
回到问题
这意味着我会从问题中翻译这段代码:
(function(global) {
var turing = {
VERSION: '0.0.1',
lesson: 'Part 1: Library Architecture'
};
if (global.turing) {
throw new Error('turing has already been defined');
} else {
global.turing = turing;
}
})(typeof window === 'undefined' ? this : window);
进入这个:
(function () {
var global = typeof window === 'undefined' ? this : window;
var turing = {
VERSION: '0.0.1',
lesson: 'Part 1: Library Architecture'
};
if (global.turing) {
throw new Error('turing has already been defined');
} else {
global.turing = turing;
}
}());
我希望这个答案能解释为什么这两者是等价的,甚至可以写成:
(function (global, turing) {
if (global.turing) {
throw new Error('turing has already been defined');
} else {
global.turing = turing;
}
})(typeof window === 'undefined' ? this : window,
{VERSION: '0.0.1', lesson: 'Part 1: Library Architecture'});
它的含义仍然相同,但同时可读性要低得多。
另请参阅my other answer,我在其中解释了类似的概念。