zoukankan      html  css  js  c++  java
  • 使用JavaScript闭包,以工厂模式实现定时器对象

    原始对象写法

      一般工作中写Javascript代码,主要写全局函数,并组织函数之间的调用,确实比较低级,

    于是想利用面向对象的思想应用到JS编码中。

      在火狐浏览器开发者网站上,看到一个实例利用对象技术实现的定时器例子,网址如下,

    https://developer.mozilla.org/en-US/docs/Web/API/window.clearTimeout#Example

     也贴出代码,如下,其实现是利用对象技术,在对象定义了三个函数(开始、取消 和 时间到的触发函数),

    定时器句柄 timeoutID 作为定向的属性存储。

    优点,自然比直接使用setTimeout函数更加集成,对功能做了封装,方便维护和管理。

    缺点,只能支持一个计时器实例,且封装的过程,没有做到信息隐藏(对象中的 句柄 和 三个函数都可以被调用)。

    <html>
    <head> 
         <style>
             
         </style>
    </head> 
    <body>
         
         <script type='text/javascript'> 
            var alarm = {
              remind: function(aMessage) { alert(aMessage); delete this.timeoutID;
              },
    
              setup: function() {
                this.cancel();
                var self = this;
                this.timeoutID = window.setTimeout(function(msg) {self.remind(msg);}, 1000, "Wake up!");
              },
    
              cancel: function() {
                if(typeof this.timeoutID == "number") {
                  window.clearTimeout(this.timeoutID);
                  delete this.timeoutID;
                }
              }
            };
            window.onclick = function() { alarm.setup() };
         </script>
    </body>
    </html>

    闭包方法

     下面应用工厂模式和闭包技术,解决上例中的缺点, 支持多实例计时器应用 并 明确开放定时器开放的接口。

    如下代码中, 

    1、createTimer 为工厂函数 , 入参为计时结束触发的函数, 和options可选参数(目前只有timeout,默认为1秒);

    2、每次调用createTimer生成一个定时器对象,即createTimer中 return出来的对象,

    此对象定义的属性和函数,即为定时器开放接口 , 包括 start 和 stop。

    3、定时器句柄 timerHandle 作为内部变量,不直接开放, 通过getTimerID函数访问,同时start赋值,被stop清空。

    4、函数 fnAlarm ,也是作为内部变量,不开放,此为计时结束触发函数,createTimer第一参数(fnAlarmIn)的重载赋值,

    此函数调用后会清空定时器句柄。

      开放接口 start stop 和 getTimerID, 被return到createTimer之外,

    同时它们对createTimer内部变量的timerHandle 和 fnAlarm的依赖,构成了闭包。

    闭包我理解是构造出来的一个封闭的运行环境, 即被return出去的这些函数,依赖的运行环境仍然跟这些函数绑定,没有切换到全局环境中去,这些函数可以继续对被return之前的环境变量进行读写。

    -- 概念中心是构造一个运行环境,此环境包括 全局变量 和 函数入参 和 函数局部变量。

    闭包的定义: 参考 下面文档, 其中牵扯到定义环境和引用环境, 当不一样,才构成这个:

    http://www.ibm.com/developerworks/cn/linux/l-cn-closure/index.html

      显然闭包的技术天然含义满足设计模式中的工厂模式。

    <html>
        <head>
            <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
        </head>
        <body>
        <label>timer msg :</label> </br>
        <textarea id="timer_msg" style="500px; height:300px"></textarea>
        <script>
        $.fn.extend({printMsg : function(msg){
             var oldmsg = $(this).val();
             var newmsg = oldmsg + "
    " + msg;
             $(this).val(newmsg);
        }});
        
        function createTimer(fnAlarmIn, options)
        {
            /* external settable */
            var timeout = 1000;
            var fnAlarm = function(){
                $("#timer_msg").printMsg(timerHandle.toString() 
                    + "-please add custom fnAlarm")
            };
            
            /* inner maintain variable */
            var timerHandle;
            
            /* set external variable */
            if ( fnAlarmIn )
            {
                fnAlarm = fnAlarmIn;
            }
    
            if ( options && options.timeout )
            {
                timeout = options.timeout;
            }
            
            return {
                start: function(){
                    timerHandle = setTimeout(function(){
                        fnAlarm();
                        delete this.timerHandle;
                    },timeout);
                    $("#timer_msg").printMsg(timerHandle.toString() + "-timer started");
                },
                stop: function(){
                    clearTimeout(timerHandle)
                    delete this.timerHandle;
                    $("#timer_msg").printMsg(timerHandle.toString() + "-timer stoped");
                },
                getTimerID: function(){
                    return timerHandle;
                }
            };
        }
    
        var timer1 = createTimer();
        timer1.start();
        timer1.stop();
        
        var timer2 = createTimer(function(){
            $("#timer_msg").printMsg("timer custom alarm");
        }, {timeout:2000});
        timer2.start();
        
        var timer3 = createTimer(function(){
            $("#timer_msg").printMsg(timer3.getTimerID()+"-timer custom alarm");
        }, {timeout:3000});
        timer3.start();
        </script>
        </body>
    </html>  

    改进

       上例中,定时器内部的函数中的打印都带有定时器句柄前缀,目的是对于多实例情况下跟踪各个实例的运行状态;

    定时器内部是这样,但是定时器触发函数中的打印是没有句柄的,如timer2中触发函数的打印;

    如果需要句柄来区分,就像timer3中,利用getTimerID接口来获取,然后打印。

      此获取句柄方法可行,但是每次都要使用 timer3.getTimerID() 来调用,显然不满足面向对象的思想,

    因为此函数是在timer3创建之后的响应函数,理应使用 this.getTimerID() 来获取。

      下面就来解决这个问题,首先参考获取网站上关于 setTimeout 计时结束触发函数中 this的解决办法,见下网址 

    https://developer.mozilla.org/zh-CN/docs/DOM/window.setTimeout

      其基本思路为, 重载setTimeout,在新的函数中, 保存当前调用setTimeout的对象,

    然后在真实的setTimeout的计时结束触发函数中,使用this来引用此对象,此时就构成了闭包。

      引入setTimeout重载代码,并将setTimeout.call到定时器对象上, 并通过fnAlarm.call,将对象传递到用户自定义的函数中。

    <html>
        <head>
            <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
        </head>
        <body>
        <label>timer msg :</label> </br>
        <textarea id="timer_msg" style="500px; height:300px"></textarea>
        <script>
        $.fn.extend({printMsg : function(msg){
             var oldmsg = $(this).val();
             var newmsg = oldmsg + "
    " + msg;
             $(this).val(newmsg);
        }});
        
        var __nativeST__ = window.setTimeout;
        window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
          var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
          return __nativeST__(vCallback instanceof Function ? function () {
            vCallback.apply(oThis, aArgs);
          } : vCallback, nDelay);
        };
        
        function createTimer(fnAlarmIn, options)
        {
            /* external settable */
            var timeout = 1000;
            var fnAlarm = function(){
                $("#timer_msg").printMsg(timerHandle.toString() 
                    + "-please add custom fnAlarm")
            };
            
            /* inner maintain variable */
            var timerHandle;
            var timerObj;
            
            /* set external variable */
            if ( fnAlarmIn )
            {
                fnAlarm = fnAlarmIn;
            }
    
            if ( options && options.timeout )
            {
                timeout = options.timeout;
            }
            
            return {
                start: function(){
                    timerHandle = setTimeout.call(this, function(){
                        $("#timer_msg").printMsg(this.getTimerID() + "-->timer id");
                        fnAlarm.call(this);
                        delete this.timerHandle;
                    },timeout);
                    $("#timer_msg").printMsg(timerHandle.toString() + "-timer started");
                },
                stop: function(){
                    clearTimeout(timerHandle)
                    delete this.timerHandle;
                    $("#timer_msg").printMsg(timerHandle.toString() + "-timer stoped");
                },
                getTimerID: function(){
                    return timerHandle;
                }
            };
        }
    
        var timer1 = createTimer();
        timer1.start();
        timer1.stop();
        
        var timer2 = createTimer(function(){
            $("#timer_msg").printMsg("timer custom alarm");
        }, {timeout:2000});
        timer2.start();
        
        var timer3 = createTimer(function(){
            $("#timer_msg").printMsg(timer3.getTimerID()+"-timer custom alarm");
        }, {timeout:3000});
        timer3.start();
        
        var timer4 = createTimer(function(){
            $("#timer_msg").printMsg(this.getTimerID()+"-timer custom alarm");
        }, {timeout:4000});
        timer4.start();
        </script>
        </body>
    </html>

    除了上面 的触发函数的对象绑定功能,

    setTimeout执行函数带扩展参数,解决IE6、7不支持扩展参数的缺陷, 也是用闭包功能,见:

    https://developer.mozilla.org/zh-CN/docs/DOM/window.setTimeout

  • 相关阅读:
    图像、视频等文件类型(拓展名)
    图像、视频等文件类型(拓展名)
    Mstar 编译器的搭建
    microsoft windows network 不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接
    Ubuntu 14.04报“leaking memory”错误
    linux下创建与删除用户详细步骤 ***
    GX 编译器 的搭建
    VMware网络模式介绍
    ubuntu 源更新(sources.list)
    目录的执行权限
  • 原文地址:https://www.cnblogs.com/lightsong/p/3703045.html
Copyright © 2011-2022 走看看