深入理解模块模式

        这里主要引用《JavaScript语言精粹》里面有关函数的理解,结合《JavaScript高级程序设计-第2版》对块级作用域的理解,联想到自己所做的项目,对模块模式作以汇总并评价。

  1 /**
  2  * 首先需要知道块级作用域的概念——
  3  *     1>JavaScript当中没有真正意义上的块级作用域,我们使用匿名函数进行模拟实现。
  4  *     2>在块级作用域中定义的任何变量,都会在执行结束时被销毁(闭包另当别论),源于没有指向该匿名函数的引用。
  5  *     3>当里面应用了闭包时,该块级作用域里的代码执行结束时,销毁的是它的作用域链,但是其活动对象仍然保存在内存中。
  6  *     3>作用:
  7  *         a>提高性能(相对而言):对于临时需要的变量,用完立即销毁
  8  *         b>防止向全局作用域中添加变量和函数。
  9  *         c>减少闭包占用的内存问题
 10  *     4>引入模块模式...
 11  */
 12 (function($){
 13        /**
 14         * 模块模式
 15         *     1>组成
 16         *         a>外层是一个立即执行的函数表达式
 17         *         b>模块中定义私有变量(当然也有私有函数)
 18         *         c>利用闭包创建可以访问私有变量和函数的特权函数。
 19         *         d>返回这个特权函数
 20         *    2>作用
 21         *        a>摒弃全局变量
 22         *        b>封装功能或者程序,毫无疑问,更具维护性,更具重用性
 23         *    3>优势:只暴露了公有方法,隐藏里面的私有属性
 24         */
 25        //举个例子
 26        //输入区域键盘处理
 27        dom.find('div.ldj-chat-in input, div.ldj-chat-input textarea').bind('keydown keyup change',function(dom){
 28            //这3个变量是专属于这个模块的私有变量
 29            var curr = 0;
 30            /**
 31              * 这里的history是一个数组,用于保存历史记录,很显然,这里的history需要静态的保存历史数据,而不是每一次触发事件的时候,再重新赋值所有历史记录值,
 32              * 可能如果是新手的话,可能会把这里的history保存为一个全局变量,以便于操作,保存记录。但是,由于全局变量是共享的,所以如果无意中改变了它的值,
 33              * 后果将是很严重的。这也就是我们通常说的,"全局变量是魔鬼"。我们利用了模块模式(一个立即执行的函数表达式),摒弃了全局变量的应用。
 34              * 当然我们可以利用传入的参数值来保存对应变量的状态。接着我们要实现我们的功能就是,让history具有全局变量的效果。当然也就想到应用闭包了。
 35              * 当这个函数调用完成后,由于这个函数里面存在一个匿名函数,并且里面的匿名函数调用了这里的history变量,
 36              * 而且,这个匿名函数赋值给了一个jQuery对象的一个事件,导致这里的history变量一直存在于内存当中。
 37              * 当然,这里的curr和now变量也都被引用了,那么这两个变量也将在内存里面。(这里,哪些变量需要放置在这儿,需要依照应用来定)。
 38              */
 39            var history;  
 40                
 41            var now;
 42            
 43            /**
 44             * 返回的是一个函数(闭包),这个函数能够访问到以上3个私有属性变量,拿《JavaScript语言精粹》的话来讲,就是特权函数了,
 45             * 因为只有它才能访问当这个模块里的私有变量,走出了这个模块,谁也访问不到了。
 46             */
 47            return function(e){
 48                /**
 49                 * 以下声明的这3个变量,是属于这个匿名函数的动态作用域里面的变量,也就是说,每触发一次事件,这里的值都会重新赋值,
 50                 * 当然这里的变量赋值也需要依应用而定。就像这里的text变量取的是每进行一次键盘的敲击,
 51                 * 都需要重新记录文本框里面所有敲击的内容,当然就应该是动态变量的赋值了。
 52                 */
 53                var l = $(this).data('len') || 0;
 54                var text = $(this).val();
 55                var n = text.length;
 56                history = history || dom.data('history');
 57                    
 58                if ($.browser.opera || (e.type === 'keyup')) {
 59                    //回车
 60                    if (e.keyCode === 13) {
 61                        curr = 0;
 62                        //如果消息为空,则不发送
 63                        if (text.trim() === '') {
 64                            $(this).val('').focus();
 65                            return;
 66                        }
 67                        $(this).trigger('send');
 68                        return false;
 69                    }
 70                    //
 71                    if (e.which === 38 && curr < history.length) {
 72                        now === undefined && (now = $(this).val());
 73                        var prev = history[curr];
 74                        if ($(this).is('textarea')) {
 75                            $(this).val(/^#c[0-9a-fA-F]{6}/.test(prev) ? prev.substring(8) : prev);
 76                        }
 77                        else {
 78                            $(this).val(prev);
 79                        }
 80                        $(this).data('len', prev.length);
 81                        curr++;
 82                    }
 83                    //下...
 84                }
 85            }  
 86        }(dom));  //执行完这个函数的时候,该函数的作用域链会立即销毁,但是它的活动对象一直存在于内存当中。需要强调这里传入的参数的应用理解,
 87                  //它往往传入的是需要保存状态的参数。另一个在性能的优化上理解的话,就是查询该元素更加快速,很显然,减少了一层闭包的变量查询。
 88        /**
 89         * 虽然,history变量一直在内存里面,但是在上面的块级作用域的外面是根本访问不到的。访问不到的原因是,
 90         * 上一个模块的最外层是由立即执行的函数表达式构成,导致这个匿名函数在执行完成后,
 91         * 它的作用域链会被销毁,当然里面的动态局部变量也是被销毁的,如果有闭包的存在,那么它的活动对象依旧在内存中。
 92         * 深入理解了这一点,才能对JavaScript代码的调优作以深入分析。
 93         */
 94             
 95        (function(){
 96            //第二个模块
 97            //...
 98        })();  //当然可以在这里传入必要的全局参数
 99        
100        (function(){
101            //第三个模块
102            //...
103        })();
104        
105        /**
106         * 如此这样模块化去写JavaScript代码,是不是感觉代码的封装性更好了呢?答案是肯定的。
107         * 同样的,模块模式的应用提供了一种JavaScript代码的优化思想。
108         */
109    
110    })(jQuery);  //最外层同样也是模块模式,同样的,有效摒弃全局变量;起到了块级作用域的效果。
111    
112    /**
113     * 1.模块模式的另外一个应用便是功能的有效独立扩展,汤姆大叔的博客解释的清晰明了
114     *   http://www.cnblogs.com/TomXu/archive/2011/12/30/2288372.html
115     * 2.会发现,在最外层的代码(function($){...})(jQuery),同样也是模块模式的应用,可以知道,
116     *   模块模式的应用不仅可以应用在程序的局部,同样也能应用在程序的全局。它的好处已经解释过了,在此略过。
117     */

        注意,不要仅仅用模块模式去优化我们的JavaScript代码,由于里面有闭包的存在,对于内存的的消耗还是有些大的。当然,我们需要变相的去看待这里的闭包,如果一个变量,我们持续需要检索,我们当然要选择让这种类型的变量一直置于内存中,以提高查询检索速度;不要忘了闭包的存在让这个变量实现静态存储(实现静态存储是有条件的,这里就不再解释了),什么时候需要应用这种方式,不仅要从性能上去考虑,还要从功能需求上考虑。

posted @ 2012-08-10 19:13  金广国  阅读(444)  评论(1编辑  收藏  举报