理解JavaScript模块模式
JavaScript模块模式原先是由Yahoo的Douglas Crockford提出的,在Yahoo的博文中有关于此模式的描述(http://yuiblog.com/blog/2007/06/12/module-pattern/),这种模式也应用到了YUI组件开发中,最核心的理念是用JavaScript的”类”封装私有和公有的属性和方法。
这种模式是JavaScript实现中的一种最佳实践方式.能够清晰地表达JavaScript面向对象概念 。它不允许开发人员定义全局变量去”污染”全局对象。通过增强这种模式,可以提高web的性能,同时这种模式实现的JavaScript代码也是易于维护的。
在JavaScript程序设计语言中,函数可以作为一个模块,在某些情况下,需要创建的是单例对象,而不是创建一个类的实例。在模块的内部,公共接口可以访问模块中的私有变量以及私有方法。这些公共接口函数又叫做特权方法(Privileged Methods)
许多JavaScript实现的应用程序使用的都是单例,因此JavaScript模块模式应用得就比较多。因此,创建一个匿名函数用来返回一个字面量对象包装的特权方法集,由于闭包原则,这些特权方法有权访问到匿名函数中的变量已经方法,这就是模块模式。
为什么使用模块模式?
1. 通过此模式可以把面向对象的一些理念在JavaScript语言中表述得更清晰,比如封装的思想。
2. 通过特权方法可以访问到私有变量以及私有方法,因为你不希望这些私有变量以及方法被外部无限制地访问。
使用此模式时需要注意的就是闭包的实现,即返回的特权方法。
自执行函数
在模块模式中,通过自执行方法来创建全局单例对象,通常创建的单例对象都是全局的。自执行方法通常用来创建匿名函数的。看一个例子:
(function(){ //函数体的实现 })();
理解了上面的自执行函数后,下面看一下模块模式
var jsModule = function(){//全局单例对象 var privateVar = "Foo"; var privateMethod = function(){ return privateVar; }; return { printVar: function(){ return privateMethod(); } }; }();//自执行函数 console.log(jsModule.printVar()); 也可以这样: var jsModule = function(){ var privateVar = "Foo"; var privateMethod = function(){ return privateVar; }; var obj = {//注意这里的字面量对象可以是通过工厂模式创建的对象 printVar: function(){ return privateMethod(); } }; obj.appendBar = function(){//对返回的对象进行扩展。 return "Bar"; } return obj; }(); console.log(jsModule.printVar());//Foo console.log(jsModule.appendBar());//Bar
创建子模块
在原有模块基础上加强模块功能
var jsModule = (function(){ var privateVar = "Foo"; var privateMethod = function(){ return privateVar; } return { printVar: function(){ return privateMethod(); } } })(); jsModule.subModule = function(){//在现有模块基础上添加子模块,相当于给一个字面量对象添加一个属性,这个属性值还是一个字面量对象 var anotherPrivateVar = "Bar"; var sayWord = function(str){ switch(str) { case "Foo": return "Foo Bar"; default: return "Hello World"; } }; return { say: function(){ return sayWord(jsModule.printVar()); } } }(); console.log(jsModule.subModule.say());
扩展模块
通过把现有模块包装到一个新的模块来扩展现有模块。可以访问现有模块中的方法,以及重写现有模块方法:
var newJsModule = function(super_module){ var parent_module = super_module;//下面就可以访问父模块特权方法了 parent_module.printVar = function(){//重写父模块方法 return "Bar"; }; var privateMethod2 = function(){ return "Hello JavaScript"; }; return { newPrintVar: function(){ return parent_module.printVar();//访问父模块方法 } } }(jsModule);//通过传递已有模块来扩展该模块,定义完匿名函数就立刻执行 console.log(newJsModule.newPrintVar());
模块模式的缺点
- 如果想改变属性和方法的可见性,那么就要修改每一处使用到这些属性以及方法的地方
- 在模块后来添加的方法中无法访问到原模块中的私有属性以及方法,可以通过创建子模块或者扩展模块来扩展功能。
var jsModule = function(){ var privateVar = "Foo"; var privateMethod = function(){ return privateVar; }; return { printVar: function(){ return privateMethod(); } }; }(); console.log(jsModule.printVar()); jsModule.newMethod = function(){ return privateMethod();////ReferenceError, privateMethod is not defined() } console.log(jsModule.newMethod());
小结
没有完美的解决方案,只有合适的。通过对象模式可以写出大型复杂的JavaScript程序,YUI的开发都用到此模式,所以在平时的开发中还是值得借鉴的。