匿名函数由一个()括号包起来。这是由于JavaScript语言认为以全局引入
JavaScript有一个叫隐含全局变量的特征。任什么时候候,一个变量名被使用,编译器会往回遍历做用域链,知道找到这个变量名的var声明语句。若是没找到,则该变量当成全局变量,若是是为它赋值,则会为它建立一个全局变量。这意味着,在匿名函数中,很是容易建立或使用全局变量。不幸的是,这会致使代码很是难管理,由于对于程序员来讲,不知道代码中哪一个变量是全局变量。
幸运的是,匿名函数提供了一个简单的解决办法,经过传入一个全局的参数到匿名方法来引用它们,这样一来就比引用全局变量更加清晰和快速。下面是这个例子:
(function () { // ... all vars and functions are in this scope only // still maintains access to all globals }());function为开头的语句,都是定义函数的语句。添加括号就变成了建立函数表达式。
(function ($, YAHOO) { // now have access to globals jQuery (as $) and YAHOO in this code }(jQuery, YAHOO));
模块
有时候咱们不仅须要使用全局变量,还须要声明他们。咱们能够经过大家函数的return value,很容易来公开这些全局变量。这样咱们就完成一个基本的模块化设计,下面是完整的代码示例:
公开
注意到咱们这里定义了一个MODULE的全局变量,包含了两个公开属性:一个名为MODULE.moduleMethod方法和var MODULE = (function () { var my = {}, privateVariable = 1; function privateMethod() { // ... } my.moduleProperty = 1; my.moduleMethod = function () { // ... }; return my; }());
MODULE.moduleProperty属性。另外,经过匿名闭包,还能够维护着一个私有的内部状态。同时,咱们还可使用上面的方法,轻易的引入全局变量。
高级应用
虽然上面的模式可以应付许多使用场景,不过咱们能够更深刻的了解该默认,来穿建更多强大的,具备扩张性的构件。还需继续上面的模块MODULE,让咱们一个个使用。
扩展
目前该模式的一个限制就是,整个模块都必须在一个文件中。任何曾经在大型代码量下工做的人,都知道把代码分开成几个文件的价值。幸运的是,咱们有一个很好的办法来扩展模块。首先,咱们引入模块,而后继续添加属性,再把它暴露出去。下面是例子,传入的参数是上面的MODULE
var MODULE = (function (my) { my.anotherMethod = function () { // added method... }; return my; }(MODULE));
为了保持代码一致,咱们在MODULE前加上var,虽然能够去掉。上面代码运行后,咱们的模块会多出一个公开函数:MODULE.anotherMethod,这个扩展文件同事会维护者它本身私有的内部状态和引入的内容。
松散扩展
虽然咱们上面例子要求先初始化模块,而后在扩展模块新的内容,可是并非非得这样。JavaScript应用程序最后的一个地方,就是能够异步加载脚本文件。咱们能够经过松散扩展,建立灵活的,能够已任何顺序加载的,多文件模块。每一个文件须要按照下面的代码结构:
在这个模式下,var关键字都必须写上。由于这里的会建立模块,若是该模块还未存在。这意味着你可使用想LABjs这样的工具,异步加载模块文件,而不用阻塞进程。
var MODULE = (function (my) { // add capabilities... return my; }(MODULE || {}));
紧耦合扩展
虽然松散扩展挺好的,可是也存在一些限制。最重要的是,你不能安全的重写模块的属性。同时,在初始化的过程当中,你不能使用模块的其它属性(可是你能够在初始化完成后的运行时使用)。紧耦合扩展须要按必定顺序加载,可是它支持重载。下面是一个简单例子(参数仍是以前的MODULE):
var MODULE = (function (my) { var old_moduleMethod = my.moduleMethod; my.moduleMethod = function () { // method override, has access to old through old_moduleMethod... }; return my; }(MODULE));
这里咱们重写了MODULE.moduleMethod方法,同时,保留着旧函数的一个引用,以便将来须要。
克隆与继承
这个模式多是最缺少灵活性的一个了。它运行一些整齐的组合,可是确牺牲了灵活性。正如上面的代码,对象或者函数的属性不会重复,他们存在同个对象中的两个引用。修改其中一个,同时也会改动另一个。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));
跨文件私有状态html
才分模块成多个文件的一个最主要的限制是,它们每一个文件都维护者本身的私有状态,并且不能访问其它文件的私有状态。这是能够修复的。
下面就是一个使用松散扩展模块,同时能够维护全部扩展的私有状态的例子:程序员
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._seal(),组织外部环境修改内部的_privaet变量。若是module有添加新的扩展,在程序的生命周期内,人和一个内部函数,任何文件中,在加载文件以前调用_unseal()方法,而后执行完成后再调用_seal()。这个方法是今天上班的时候想出来的,我还没在别的地方看过。我想它是一个颇有用的模式,所以值得单独来写这一块内容。ajax
子模块编程
咱们最后一个高级应用实际上是最简单的。在不少状况下建立子模块是很是有用的。它就跟建立一个普通的模块同样数组
MODULE.sub = (function () {
var my = {};
// ...安全
return my;
}());闭包
虽然这很简单,可是仍是值得把它包含进来。子模块因为普通模块的有点,包含扩展功能和私有状态。异步
总结ide
大多数的高级应用均可以和其它应用结合一块儿,从而建立更好的模式。若是必定要我指出一个设计复杂应用程序的组合,
我会合并松散模式,私有状态和子模块。模块化
在这里我没有涉及到性能问题,可是我想在这里说:这些模块化模式性能都很好。他们能很好的压缩,让加快下载代码的时间。
使用松散扩展运行非阻塞并行下载文件,同时也提升下载的速度。初始化时间可能慢于其它方法,可是值得的。运行时应该也不会
有什么问题,由于全局变量被正确的包含进去。并且子模块由于缩短了本地变量的引用链,反而能提升一些速度。
最后,下面是一个子模块的例子,针对于它的父模块(若是不存在则建立),它本身能够动态加载自己。
这里没有包含私有状态,可是包含进来是很简单的。这个模式运行整个复杂的代码结构异步的加载自己以及其子模块。
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));
我但愿本文对你有帮助,请留言大家的想法。如今,更好去的编写模块化程序吧!
Reference: http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html