本文翻译自大牛ben cherry的博客:http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth 。对自己的英文很有自信的同学可以直接去这个地址看原文。
翻译能力有限,欢迎指教~~
================ 正文 =================
Module pattern 是javascript的特有编码模式。(因为它没有java那样的访问指示符,如private、public等。module pattern就是来管理变量作用域的有效方式。)它的道理很简单,但是有一些高级特性却容易被忽视。下面,本文将对这些真正有用的特性进行详细讲述。
The Basics
Anonymous Closures(匿名闭包)
(function () {
// ... all vars and functions are in this scope only
// still maintains access to all globals
}());
注意最后的()就是执行这个函数。这是语法要求,否则就仅是一个函数声明而已。
Global Import (导入全局变量)
(function ($, YAHOO) {
// now have access to globals jQuery (as $) and YAHOO in this code
}(jQuery, YAHOO));
通过把全局变量以参数的方式传入函数内部,就完成了全局变量的导入。
Module Export(模块导出)
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
这样,我们就在全局空间中声明了一个MODULE模块,该模块有1个公共属性:MODULE.moduleProperty和一个公共方法:MODULE.moduleMethod。同时还有一个privateVariable和privateMethod是私有的。
Advanced Patterns
Augmentation(扩展模块)
var MODULE = (function (my) {
my.anotherMethod = function () {
// added method...
};
return my;
}(MODULE));
可以容易的看出,我们的做法是:先将MODULE以参数形式传入匿名函数,为它增加方法,然后导出。这样MODULE就多了一个公共方法:MODULE.anotherMethod,同时之前的成员变量依然存在。尽管这里使用的是一个声明语句,但其实是没有必要的。因为在运行以上代码时要求MODULE必须已经定义。否则将报错。
Loose Augmentation(松散扩展)
var MODULE = (function (my) {
my.anotherMethod = function () {
// added method...
};
return my;
}( MODULE || {} ));
在MODULE未定义时,直接使用空对象。这样脚本就能以任意顺序加载,就是这么简单!
Tight Augmentation(紧密扩展)
var MODULE = (function (my) {
var old_moduleMethod = my.moduleMethod;
my.moduleMethod = function () {
// method override, has access to old through old_moduleMethod...
};
return my;
}(MODULE));
和一般的augmentation相比它首先保持了旧有的成员,但是和松散扩展相比,它必须要求MODULE已经定义。
Cloning and inheritance
var MODULE_TWO = (function (old) {
var my = {},
key;
for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[key];
}
}
var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
};
return my;
}(MODULE));
这个方法的灵活性很差,比如hasOwnProperty无法检测原型链上的属性,同时该方法也不能复制内部的对象成员和函数。(很多类库和教材上都提供了更好的克隆方法,不知道作者为什么把这个放到Module pattern中讲)
cross-file private state(跨文件访问私有属性)
var MODULE = (function (my) {
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
};
// permanent access to _private, _seal, and _unseal
return my;
}(MODULE || {}));
其中_private是函数内部的状态变量,通常是无法被其他脚本访问的,但是通过MODULE的_private作为媒介就可以突破这个限制了。当然,Ben还提供了2个函数用来打开和关闭这个通道。当然,这个功能只有在load了以上脚本时才有效。
sub-modules(子模块)
这个是最简单的,要吃饭了。原文奉上Our final advanced pattern is actually the simplest. There are many good cases for creating sub-modules. It is just like creating regular modules:
MODULE.sub = (function () {
var my = {};
// ...
return my;
}());
While this may have been obvious, I thought it worth including. Sub-modules have all the advanced capabilities of normal modules, including augmentation and private state.
总结
var UTIL = (function (parent, $) {
var my = parent.ajax = parent.ajax || {};
my.get = function (url, params, callback) {
// ok, so I'm cheating a bit :)
return $.getJSON(url, params, callback);
};
// etc...
return parent;
}(UTIL || {}, jQuery));