图案
(function (foo) {
...code...
foo.bar = baz;
...more code...
}(window.FOO = window.FOO || {});
您描述的模式没有正式名称,因为它是三个独立的模式组合在一起。每个模式都有多个名称,但在这篇文章中,我将使用以下术语:
关闭
整个模式的基础是closure
. 它只是一个用于限定变量和函数范围的函数,这样它们就不会污染全局命名空间:
没有关闭
//these declare window.foo and window.bar respectively
//as such, they pollute the global namespace
var foo;
function bar() {}
闭包,在这种情况下,是立即调用的功能表达式 (IIFE)
(function () {
//these declare foo and bar within the function
//but they are not accessible outside the function
var foo;
function bar() {}
}());
将变量保存在闭包中的好处是您不必担心有人会覆盖您正在使用的变量。这对于诸如i
或j
经常使用的临时变量尤其重要。
别名
这种模式的第二个重要部分是混叠。别名允许在闭包中定义和使用变量,而无需担心它驻留在哪个全局命名空间中。
无别名
(function () {
...
foo = window.SomeFunction(bar, baz);
...
}());
带别名
(function (sf) { //local name
...
foo = sf(bar, baz);
...
}(window.SomeFunction)); //global namespace
这一点尤其重要,因为这意味着可以通过在单个位置更改名称来跨大型 JavaScript 文件更改全局命名空间。这是一件好事™。此外,缩小器可以将内部别名缩短为单个字母变量名,例如a
,从而在缩小时显着节省字节。
命名空间扩展
命名空间扩展模式依赖于 or 运算符 ( ||
) 的合并行为。在许多语言中,&&
返回||
一个true
or false
,但在 JavaScript 中,&&
返回第一个falsey
值(false
, 0
, ''
, null
, undefined
),然后||
返回第一个truthy
值(任何不是falsey
)。对于这两个运算符,如果未找到相应的类型,则返回最后一个参数。这使得||
操作符成为一种方便的方式,仅当它不存在时才定义一个新的命名空间。
没有命名空间扩展
if (typeof window.Foo === 'undefined') {
window.foo = {};
}
带有命名空间扩展
window.foo = window.foo || {};
这很有用,因为它允许使用其他属性和方法扩展命名空间,而不必担心定义属性和方法的顺序。
在第一个示例中,FileA
需要在之前执行FileB
:
文件A.js
window.foo = {};
window.foo.bar = 'baz';
文件B.js
window.foo.fizz = 'buzz';
在第二个示例中,File1
可以File2
按任何顺序执行:
文件1.js
window.foo = window.foo || {};
window.foo.bar = 'baz';
文件2.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 || {}));