Pattern
(function (foo) {
...code...
foo.bar = baz;
...more code...
}(window.FOO = window.FOO || {});
您描述的模式没有正式名称,因为它是三个独立模式的组合。每个模式都有多个名称,但在这篇文章中我将使用以下术语:
Closure
整个模式的基础是closure
。它只是一个用于限定变量和函数范围的函数,这样它们就不会污染全局命名空间:
No closure
//these declare window.foo and window.bar respectively
//as such, they pollute the global namespace
var foo;
function bar() {}
Closure, in this case, an Immediately Invoked Functional Expression (IIFE)
(function () {
//these declare foo and bar within the function
//but they are not accessible outside the function
var foo;
function bar() {}
}());
将变量保留在闭包中的优点是您不必担心有人覆盖您正在使用的变量。这对于临时变量尤其重要,例如i
or j
经常使用的。
Alias
该模式的第二个重要部分是混叠。别名允许在闭包中定义和使用变量,而无需担心它驻留在哪个全局名称空间中。
Without Aliasing
(function () {
...
foo = window.SomeFunction(bar, baz);
...
}());
With Aliasing
(function (sf) { //local name
...
foo = sf(bar, baz);
...
}(window.SomeFunction)); //global namespace
这一点尤其重要,因为这意味着可以通过更改单个位置的名称来更改大型 JavaScript 文件的全局命名空间。这是一件好事™。此外,缩小器可以将内部别名缩短为单个字母变量名称,例如a
,从而在缩小时节省大量字节。
命名空间扩展
命名空间扩展模式依赖于 or 运算符的合并行为 (||
)。在许多语言中,&&
and ||
返回任一true
or false
,但在 JavaScript 中,&&
返回第一个falsey
value (false
, 0
, ''
, null
, undefined
), and ||
返回第一个truthy
值(任何不是falsey
)。对于这两个运算符,如果未找到相应的类型,则返回最后一个参数。这使得||
运算符是定义新名称空间的便捷方法仅当它尚不存在时.
Without namespace extension
if (typeof window.Foo === 'undefined') {
window.foo = {};
}
With namespace extension
window.foo = window.foo || {};
这很有用,因为它允许使用附加属性和方法扩展命名空间,而不必担心属性和方法定义的顺序。
在第一个示例中,FileA
需要在之前执行FileB
:
FileA.js
window.foo = {};
window.foo.bar = 'baz';
FileB.js
window.foo.fizz = 'buzz';
在第二个例子中,File1
and File2
可以按任意顺序执行:
File1.js
window.foo = window.foo || {};
window.foo.bar = 'baz';
File2.js
window.foo = window.foo || {};
window.foo.fizz = 'buzz';
现在都在一起了
一起使用每种模式可以创建一个非常强大的模块化脚本:
//use foo internally so that you don't have to worry about
//what the global namespace is called
(function (foo) {
//declare variables internally that you want to keep local to the script
var i,
len,
internal,
qux;
//declare functions/properties on the alias when you want to expose them
foo.bar = function () {...};
//extend the global namespace so that existing extensions are persistent
}(window.FOO = window.FOO || {}));