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 = 0return {
        get: function () {
            return some;
        },
        set: function (val) {
            some = val;
        },
        increment: function () {
            return ++some;
        }
    };
} ;


var counter = new Counter();  //此时counter不是新创建的对象,而是有字面量方法创建的对象
counter.get();

 

每次用的时候都要new一下,也就是说每个实例在内存里都是一份copy,如果你不需要传参数或者没有一些特殊苛刻的要求的话,建议用第一种方法自执行来达到目的,这样该实例在内存中只会存在一份copy,节省内存。

Module模式的基本特征:

  1. 模块化,可重用
  2. 封装了变量和function,和全局的namaspace不接触,松耦合
  3. 只暴露可用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模式效率高,代码少,加载速度快。使用松耦合扩展允许并行加载,这更可以提升下载速度。不过初始化时间可能要慢一些,但是为了使用好的模式,这是值得的。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。