模块模式

模块模式是用来封装逻辑并避免全局命名空间污染的好方法。 使用匿名函数可以做到这一点, 匿名函数也是JavaScript 中被证明最优秀的特性之一。 通常是创建一个匿名函数并立即执行它。 在匿名函数里的逻辑都在闭包里运行, 为应用中的变量提供了局部作用域和私有的运行环境 :

1
2
3
(function(){
/* ... */
})();

全局导入

模块内部的变量都是局部变量,全局命名空间无法访问,但是模块内部可以访问全局变量,模块中的变量很难一眼就看出来是全局变量还是局部变量,隐身的全局变量由于解释器要遍历作用域链的原因,会是程序变得更慢,局部变量的读取往往更快更高效。

将全局变量以函数参数的方式传入匿名函数,可以将全局变量导入模块中。这种方式比隐式的全局对象更加简洁而且速度更快 :

1
2
3
(function($){
/* ... */
})(jQuery);

模块中不要使用隐式全局变量,全局变量要通过参数传入匿名函数

全局导出

理想状况下应当使用尽可能少的全局变量, 但总会有某些特殊场景用到全局变量。 可以将页面的 window 导入我们的模块, 直接给它定义属性, 通过这种方式可以暴露全局变量 :

1
2
3
(function($, exports){
exports.Foo = "wem";
})(jQuery, window);

添加少量上下文

使用局部上下文是一种架构模块很有用的方法, 特别是当需要给事件注册回调函数时。

实际情况是, 模块中的上下文都是全局的, this 就是 window :

1
2
3
(function(){
assertEqual( this, window );
})();

如果想自定义作用域的上下文, 则需要将函数添加至一个对象中, 比如 :

1
2
3
4
5
6
7
(function(){
var mod = {};
mod.contextFunction = function(){
assertEqual( this, mod );
};
mod.contextFunction();
})();

在 contextFunction() 中的上下文不是全局的, 而是 mod 对象。 这时使用 this 就不必担心会创建全局变量了。 为了展示如何在实际中更好地使用这种技术, 我们对例子做进一步修改 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(function ($) {
var mod = {};
mod.load = function (func) {
$($.proxy(func, this));
};
mod.load(function () {
this.view = $("#view");
});
mod.assetsClick = function (e) {
// 处理点击
};
mod.load(function () {
this.view.find(".assets").click(
$.proxy(this.assetsClick, this)
);
});
})(jQuery);

这里新建了 load() 函数来处理回调, 当页面加载后执行它。 注意, 我们使用了 jQuery.proxy() 来确保回调是基于正确的上下文执行的。

然后, 当页面加载时, 我们给一个元素添加了点击事件监听, 这里使用了局部函数assetsClick() 作为回调函数。

摘自:《基于MVC的JavaScript Web富应用开发》