JS的module模式
声明:本文的所有代码和见解都来自于汤姆大叔的博客,我只是将他复制过来加以自己的理解,以便方便自己记忆而已,大叔高清无码地址。
我们先用一个简单直观的例子看看module模式长得什么样(创建一个匿名函数表达式,然后return一个变量,变量中包含着你想要暴露的属性和方法):
// 创建一个立即调用的匿名函数表达式 // return一个变量,其中这个变量里包含你要暴露的东西 // 返回的这个变量将赋值给counter,而不是外面声明的function自身 var counter = (function () { var i = 0; return { get: function () { return i; }, set: function (val) { i = val; }, increment: function () { return ++i; } }; } ()); // counter是一个带有多个属性的对象,上面的代码对于属性的体现其实是方法 counter.get(); // 0 counter.set(3); counter.increment(); // 4 counter.increment(); // 5 counter.i; // undefined 因为i不是返回对象的属性 i; // 引用错误: i 没有定义(因为i只存在于闭包)
如果不进行自执行,module模式也可以表达如下:
var Counter = function () { var some = 0; return { get: function () { return some; }, set: function (val) { some = val; }, increment: function () { return ++some; } }; } ; var counter = new Counter(); //此时counter不是新创建的对象,而是有字面量方法创建的对象 counter.get();
每次用的时候都要new一下,也就是说每个实例在内存里都是一份copy,如果你不需要传参数或者没有一些特殊苛刻的要求的话,建议用第一种方法自执行来达到目的,这样该实例在内存中只会存在一份copy,节省内存。
Module模式的基本特征:
- 模块化,可重用
- 封装了变量和function,和全局的namaspace不接触,松耦合
- 只暴露可用public的方法,其它私有方法全部隐藏
在闭包中使用全局变量,为清晰明了得显示自己是使用全局变量,而不是在闭包内自己声明了一个隐式全局变量,可以将全局变量挡住哦参数传递给闭包,然后就可以在闭包中放心地使用了。
(function ($, YAHOO) { // 这里,我们的代码就可以使用全局的jQuery对象了,YAHOO也是一样 } (jQuery, YAHOO));
如果我们不仅仅想使用全局变量,也希望声明全局变量,那就应该这样写:
var blogModule = (function () { var my = {}, privateName = "博客园"; function privateAddTopic(data) { // 这里是内部处理代码 } my.Name = privateName; my.AddTopic = function (data) { privateAddTopic(data); }; return my; } ());
这里闭包内的匿名函数自执行(当然啦,这里不使用闭包也是可以的),然后返回一个全局变量blogModule,该全局变量带有一个属性和一个方法。
扩展功能
以上模式的缺点就是所有代码都要在同一个文件中,如果是大型项目,需要多人协作的话,就应该用下面的模式,将其自身当做参数传入,然后为其添加方法。但这个模式要求全局变量应该事先声明,否则当做参数传入的时候就会发生错误,顺序不能乱:
var blogModule = (function (my) {
var i = 0; my.AddPhoto = function () { //添加内部代码,可以使用局部变量i }; return my; } (blogModule));
这样的话在不同文件中都可以为该全局对象添加方法。但是必须先声明blogModule,然后再执行上面的扩展代码,也就是说步骤不能乱为了解决这个问题,可以使用下面的松耦合扩展。
松耦合扩展
var blogModule = (function (my) { // 添加一些功能 return my; } (blogModule || {}));
实际上思想就是:var cnblogs = cnblogs || {} ;如果cnblogs已经存在了,就使用它,不存在就赋值给一个空的对象。这样就不存在声明顺序问题。
但是这又带来了另外一个问题,如果你要重写方法,这就要求顺序一定不能变,就一定要有先后顺序之分,所以就有了下面的紧耦合扩展(重写方法必须在方法已经声明了之后)
紧耦合扩展
var blogModule = (function (my) { var oldAddPhotoMethod = my.AddPhoto; my.AddPhoto = function () { // 重写方法,依然可通过oldAddPhotoMethod调用旧的方法 }; return my; } (blogModule));
跨文件共享私有对象
下面例子中,跨文件共享私有变量private,可以通过上锁和解锁来进行。这段代码需要好好理解一下才能搞明白,自己琢磨吧~
var blogModule = (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; }; return my; } (blogModule || {}));
子模块
其实不能说是一个新的知识点,知识说在对象的属性(该属性为一个对象)上应用module模块而已,上面讲的所有模式都能用于子模块,这是毫无疑问的。
blogModule.CommentSubModule = (function () { var my = {}; // ... return my; } ());
总结
上面的大部分方式都可以互相组合使用的,一般来说如果要设计系统,可能会用到松耦合扩展,私有状态和子模块这样的方式。Module模式效率高,代码少,加载速度快。使用松耦合扩展允许并行加载,这更可以提升下载速度。不过初始化时间可能要慢一些,但是为了使用好的模式,这是值得的。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。