• Javascript事件设计模式(七)


    一:事件设计概述

           事件机制可以使程序逻辑更加符合现实世界,在JavaScript中很多对象都有自己的事件,例如按钮就有onclick事件,下拉列表框就有 onchange事件,通过这些事件可以方便编程。那么对于自己定义的类,是否也可以实现事件机制呢?是的,通过事件机制,可以将类设计为独立的模块,通过事件对外通信,提高了程序的开发效率。

    二: 最简单的事件设计模式

    最简单的一种模式是将一个类的方法成员定义为事件,这不需要任何特殊的语法,通常是一个空方法,例如:
    function class1(){
          //构造函数
    }
    class1.prototype={
          show:function(){
                //show函数的实现
                this.onShow();  //触发onShow事件
          },
          onShow:function(){}  //定义事件接口
    }
    上面的代码中,就定义了一个方法:show(),同时该方法中调用了onShow()方法,这个onShow()方法就是对外提供的事件接口,其用法如下:
    //创建class1的实例
    var obj=new class1();
    //创建obj的onShow事件处理程序
    obj.onShow=function(){
          alert("onshow event");
    }
    //调用obj的show方法
    obj.show();


    由此可见,obj.onShow方法在类的外部被定义,而在类的内部方法show()中被调用,这就实现了事件机制。
    上述方法很简单,实际的开发中常用来解决一些简单的事件功能。说它简单,因为它有以下两个缺点:
    ? 不能够给事件处理程序传递参数,因为是在show()这个内部方法中调用事件处理程序的,无法知道外部的参数;
    ? 每个事件接口仅能够绑定一个事件处理程序,而内部方法则可以使用attachEvent或者addEventListener方法绑定多个处理程序。
    在下面两小节将着重解决这个问题。

    三: 给事件处理程序传递参数

         给事件处理程序传递参数不仅是自定义事件中存在的问题,也是系统内部对象的事件机制中存在的问题,因为事件机制仅传递一个函数的名称,不带有任何参数的信息,所以无法传递参数进去。例如:
    //定义类class1
    function class1(){
          //构造函数
    }
    class1.prototype={
          show:function(){
                //show函数的实现
                this.onShow();  //触发onShow事件
          },
          onShow:function(){}  //定义事件接口
    }
    //创建class1的实例
    var obj=new class1();
    //创建obj的onShow事件处理程序
    function obj.OnShow(userName){
           alert("hello,"+userName);
    }
    //定义变量userName
    var userName="jack";
    //绑定obj的onShow事件
    obj.onShow=objOnShow;  //无法将userName这个变量传递进去
    //调用obj的show方法
    obj.show();
    注意上面的obj.onShow=objOnShow事件绑定语句,不能为了传递userName变量进去而写成:
    obj.onShow=objOnShow(userName);
    或者:
    obj.onShow="objOnShow(userName)";
    前者是将objOnShow(userName)的运行结果赋给了obj.onShow,而后者是将字符串“objOnShow(userName)”赋给了obj.onShow。
    要解决这个问题,可以从相反的思路去考虑,不考虑怎么把参数传进去,而是考虑如何构建一个无需参数的事件处理程序,该程序是根据有参数的事件处理程序创建的,是一个外层的封装。现在自定义一个通用的函数来实现这种功能:
    //将有参数的函数封装为无参数的函数
    function createFunction(obj,strFunc){
          var args=[];      //定义args用于存储传递给事件处理程序的参数
          if(!obj)obj=window;     //如果是全局函数则obj=window;
          //得到传递给事件处理程序的参数
          for(var i=2;i<arguments.length;i++)args.push(arguments[i]);
          //用无参数函数封装事件处理程序的调用
          return function(){
                obj[strFunc].apply(obj,args); //将参数传递给指定的事件处理程序
          }
    }
    该方法将一个有参数的函数封装为一个无参数的函数,不仅对全局函数适用,作为对象方法存在的函数同样适用。该方法首先接收两个参数:obj和 strFunc,obj表示事件处理程序所在的对象;strFunc表示事件处理程序的名称。除此以外,程序中还利用arguments对象处理第二个参数以后的隐式参数,即未定义形参的参数,并在调用事件处理程序时将这些参数传递进去。例如一个事件处理程序是:
    someObject.eventHandler=function(_arg1,_arg2){
         //事件处理代码
    }
    应该调用:
    createFunction(someObject,"eventHandler",arg1,arg2);
    这就返回一个无参数的函数,在返回的函数中已经包括了传递进去的参数。如果是全局函数作为事件处理程序,事实上它是window对象的一个方法,所以可以传递window对象作为obj参数,为了更清晰一点,也可以指定obj为null,createFunction函数内部会自动认为该函数是全局函数,从而自动把obj赋值为window。下面来看应用的例子:
    <script language="JavaScript" type="text/javascript">
    <!--
    <!--
            //将有参数的函数封装为无参数的函数
            function createFunction(obj,strFunc){
                var args=[];
                if(!obj)obj=window;
                for(var i=2;i<arguments.length;i++)args.push(arguments[i]);
                return function(){
                    obj[strFunc].apply(obj,args);
                }
            }
            //定义类class1
            function class1(){
                //构造函数
            }
            class1.prototype={
                show:function(){
                    //show函数的实现
                    this.onShow();//触发onShow事件
                },
                onShow:function(){}//定义事件接口
            }
            //创建class1的实例
            var obj=new class1();
            //创建obj的onShow事件处理程序
            function objOnShow(userName){
                alert("hello,"+userName);
            }
            //定义变量userName
            var userName="jack";
            //绑定obj的onShow事件
            obj.onShow=createFunction(null,"objOnShow",userName);
            //调用obj的show方法
            obj.show();
    //-->
    </script>
    在这段代码中,就将变量userName作为参数传递给了objOnShow事件处理程序。事实上,obj.onShow得到的事件处理程序并不是objOnShow,而是由createFunction返回的一个无参函数。
    通过createFunction封装,就可以用一种通用的方案实现参数传递了。这不仅适用于自定义的事件,也适用于系统提供的事件,其原理是完全相同的。

    前台代码:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
        <script type="text/javascript" src="js/Core.js"></script>
        <script type="text/javascript">
           function Class01(){
    
           }
            Class01.prototype={
                show:function(){
                  this.onShow();
                },
                onShow:function(){}
                }
    var userName="json";
            var myOnshow=function(userName){
                alert("hellow"+userName);
            }
           var myEventHandle2=function(usernaem,age){
               alert(usernaem+","+age);
           }
    
            var class01=new Class01();
            class01.onShow=createFunction(null,"myOnshow",userName);
    class01.onShow=createFunction(null,"myEventHandle2","sunliyuan",20);
    
        </script>
    </head>
    <body>
    <button onclick="class01.show()">测试onShow事件</button>
    </body>
    </html>
    

     js代码:

    function createFunction(obj,strFunc){
        var args=[];
        if(!obj)obj=window;
        for(var i=2;i<arguments.length;i++)args.push(arguments[i]);
        return function(){
            obj[strFunc].apply(obj,args);
        }
    }
    

     四:使自定义事件支持多绑定

    可以用attachEvent或者addEventListener方法来实现多个事件处理程序的同时绑定,不会互相冲突,而自定义事件怎样来实现多订阅呢?要实现多订阅,必定需要一个机制用于存储绑定的多个事件处理程序,在事件发生时同时调用这些事件处理程序。从而达到多订阅的效果,其实现如下:
    <script language="JavaScript" type="text/javascript">
    <!--
    //定义类class1
    function class1(){
          //构造函数
    }
    //定义类成员
    class1.prototype={
          show:function(){
               //show的代码
               //...
               //如果有事件绑定则循环onshow数组,触发该事件
               if(this.onshow){
                      for(var i=0;i<this.onshow.length;i++){
                            this.onshow[i](); //调用事件处理程序
                      }
               }
          },
          attachOnShow:function(_eHandler){
                if(!this.onshow)this.onshow=[]; //用数组存储绑定的事件处理程序引用
                this.onshow.push(_eHandler);
          }
    }
    var obj=new class1();
    //事件处理程序1
    function onShow1(){
          alert(1);
    }
    //事件处理程序2
    function onShow2(){
          alert(2);
    }
    //绑定两个事件处理程序
    obj.attachOnShow(onShow1);
    obj.attachOnShow(onShow2);
    //调用show,触发onshow事件
    obj.show();
    //-->
    </script>
    从代码的执行结果可以看到,绑定的两个事件处理程序都得到了正确的运行。如果要绑定有参数的事件处理程序,只需加上createFunction方法即可,
    这种机制基本上说明了处理多事件处理程序的基本思想,但还有改进的余地。例如如果类有多个事件,可以定义一个类似于attachEvent的方法,用于统一处理事件绑定。在添加了事件绑定后如果想删除,还可以定义一个detachEvent方法用于取消绑定。这些实现的基本思想都是对数组的操作。

    代码:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
       <script type="text/javascript" src="js/Core.js"></script>
        <script type="text/javascript">
           function Class01(){
           }
          Class01.prototype={
              show:function(){
                  //如果有事件绑定循环onshow数组,触发该事件
                  if(this.onshow){
                      for(var i=0;i<this.onshow.length;i++){
                          this.onshow[i]();  //调用事件处理程序
                      }
                  }
              },
              attachOnShow:function(_eHandler){
                  //用数组存储绑定的事件处理程序引用
                  if(!this.onshow)this.onshow=[]; 
                  this.onshow.push(_eHandler);
    
              }
          }
    
            var class01=new Class01();
    
           function onShow01(username){
               alert("寿星:"+username);
           }
           function onShow02(username,birthday){
               alert("寿星:"+username+"!!!!!二十岁"+ birthday);
           }
           var func1=createFunction(null,"onShow01","孙丽媛");
           var func2=createFunction(null,"onShow02","孙丽媛","生日快乐:8月12日,难忘的一天");
    
            class01.attachOnShow(func1);
            class01.attachOnShow(func2);
    
        </script>
    </head>
    <body>
    <button onclick="class01.show()">生日快乐</button>
    </body>
    </html>


    js代码:
    function createFunction(obj,strFunc){
    var args=[];
    if(!obj)obj=window;
    for(var i=2;i<arguments.length;i++)args.push(arguments[i]);
    return function(){
    obj[strFunc].apply(obj,args);
    }
    }

    js面向对象的综合实例:

    一:使用面向对象思想处理cookie

    1.需求:


    对于cookie的处理,事实上只是封装一些方法,每个对象不会有状态,所以不需要创建一个cookie处理类,而只用一个全局对象来联系这些cookie操作。对象名可以理解为命名空间。对cookie操作经常以下操作。
    (1)设置cookie包括了添加和修改功能,事实上如果原有cookie名称已经存在,那么添加此cookie就相当于修改了此cookie。在设置 cookie的时候可能还会有一些可选项,用于指定cookie的生命周期、访问路径以及访问域。为了让cookie中能够存储中文,该方法中还需要对存储的值进行编码。
    (2)删除一个cookie,删除cookie只需将一个cookie的过期时间设置为过去的一个时间即可,它接收一个cookie的名称为参数,从而删除此cookie。
    (3)取一个cookie的值,该方法接收cookie名称为参数,返回该cookie的值。因为在存储该值的时候已经进行了编码,所以取值时应该能自动解码,然后返回。

    2.代码(思路):

    1. 创建Cookie对象
    因为是作为类名或者命名空间的作用,所以和Math对象类似,这里使用Cookie来表示该对象:
    var Cookie=new Object();
    2.  实现设置Cookie的方法
    方法为:setCookie(name,value,option);其中name是要设置cookie的名称;value是设置cookie的值;option包括了其他选项,是一个对象作为参数。其实现如下:
    Cookie.setCookie=function(name,value,option){
         //用于存储赋值给document.cookie的cookie格式字符串
         var str=name+"="+escape(value); 
         if(option){
                //如果设置了过期时间
                if(option.expireDays){
                       var date=new Date();
                       var ms=option.expireDays*24*3600*1000;
                       date.setTime(date.getTime()+ms);
                       str+="; expires="+date.toGMTString();
                }
                if(option.path)str+="; path="+path;   //设置访问路径
                if(option.domain)str+="; domain"+domain; //设置访问主机
                if(option.secure)str+="; true";    //设置安全性
         }
         document.cookie=str;
    }
    3. 实现取Cookie值的方法
    方法为:getCookie(name);其中name是指定cookie的名称,从而根据名称返回相应的值。实现如下:
    Cookie.getCookie=function(name){
         var cookieArray=document.cookie.split("; "); //得到分割的cookie名值对
         var cookie=new Object();
         for(var i=0;i<cookieArray.length;i++){
               var arr=cookieArray[i].split("=");    //将名和值分开
               if(arr[0]==name)return unescape(arr[1]); //如果是指定的cookie,则返回它的值
         }
         return "";
    }

    4.  实现删除Cookie的方法
    方法为:deleteCookie(name);其中name是指定cookie的名称,从而根据这个名称删除相应的cookie。在实现中,删除cookie是通过调用setCookie来完成的,将option的expireDays属性指定为负数即可:
    Cookie.deleteCookie=function(name){
         this.setCookie(name,"",{expireDays:-1}); //将过期时间设置为过去来删除一个cookie
    }
    通过下面的代码,整个Cookie对象创建完毕后,可以将其放到一个大括号中来定义,例如:
    var Cookie={
          setCookie:function(){},
          getCookie:function(){},
          deleteCookie:function(){}
    }
    通过这种形式,可以让Cookie的功能更加清晰,它作为一个全局对象,大大方便了对Cookie的操作,例如:
    Cookie.setCookie("user","jack");
    alert(Cookie.getCookie("user"));
    Cookie.deleteCookie("user");
    alert(Cookie.getCookie("user"));
    上面的代码就先建立了一个名为user的cookie,然后删除了该cookie。两次alert输出语句显示了执行的效果。
    本节通过建立一个Cookie对象来处理cookie,方便了操作,也体现了面向对象的编程思想:把相关的功能封装在一个对象中。考虑到 JavaScript语言的特点,本章没有选择需要创建类的面向对象编程的例子,那和一般面向对象语言没有大的不同。而是以JavaScript中可以直接创建对象为特点介绍了Cookie对象的实现及其工作原理。事实上这也和JavaScript内部对象Math的工作原理是类似的。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title></title>
       <script type="text/javascript" src="js/Cookie.js"></script>
    
    </head>
    <body>
    <button onclick="Cookie.setCookie('username','json');alert('set cookie successed!');">设置Cookie值</button>
    <button onclick="alert(Cookie.getCookie('username'))">获取Cookie值</button>
    <button onclick="Cookie.deleteCookie('username');alert('deleted!')">删除Cookie</button>
    </body>
    </html>
    

     js代码:

    /**
     * Created by shizhiwei on 2016/9/12.
     */
        //创建全局的实例
    var Cookie={
        setCookie:function(){},
        getCookie:function(){},
        deleteCookie:function(){}
    }
    
    //cookie的名称,值,选项   setCookie起到修改和添加的作用
    Cookie.setCookie=function(name,value,option){
        //用于存储赋值给document.cookie的cookie格式字符串
        var str=name+"="+escape(value);
        if(option){
            //如果设置了过期时间
            if(option.expireDays){
                var date=new Date();
                var ms=option.expireDays*24*3600*1000;
                date.setTime(date.getTime()+ms);
                str+="; expires="+date.toGMTString();
            }
            if(option.path)str+="; path="+path;   //设置访问路径
            if(option.domain)str+="; domain="+domain; //设置访问主机
            if(option.secure)str+="; true";    //设置安全性
        }
        document.cookie=str;
    }
    
    //取值
    Cookie.getCookie=function(name){
        var cookieArray=document.cookie.split("; "); //得到分割的cookie名值对
        var cookie=new Object();
        for(var i=0;i<cookieArray.length;i++){
            var arr=cookieArray[i].split("=");    //将名和值分开
            if(arr[0]==name)return unescape(arr[1]); //如果是指定的cookie,则返回它的值
        }
        return "";
    }
    
    Cookie.deleteCookie=function(name){
        this.setCookie(name,"",{expireDays:-1}); //将过期时间设置为过去来删除一个cookie
    }
    

    js的压缩和混淆技术:

    一:

    JavaScript Compressor
    网址:http://dean.edwards.name/packer/
    优点:简单易用
    缺点:只能在线,不够安全容易出错
     
    二:
    JSA(javascript Analyser)--JSA 是一个脚本压缩、混淆、分析工具, 也是 JSI 的编译工具, 有着非常可观的压缩质量和压缩比率。 JSA 的运用,可以减轻网络负担,保护源代码。
    官网下载地址:
    在线压缩:http://www.xidea.org/project/jsa/
     
    JSA的特点:
            相比之下JSA的是当前比较安全最有效的压缩工具。
    JSA 不仅提供代码压缩功能,还可以做格式化,脚本分析。
    脚本分析功能可以用于查看脚本信息,以及查找脚本中的潜在问题。
    比如查看脚本中申明了那些函数,变量。
    使用了那些外部变量。等等。。。
    JSA的压缩过程分两步,第一步是语法压缩,安全且有效。
    第二步是文本压缩,目前采用的是
    JavaScript Compressor的压缩算法。
    (http://dean.edwards.name/packer/ )
    这些都可以在设置窗口设置。
    默认情况先用语法压缩,当文件大于1000k且采用文本压缩仍然可以压缩到原来大小90%时才在原来基础上采用文本压缩。
     
    开启服务端的HTTP压缩功能(IIS)
     开启服务端的HTTP压缩功能(IIS)
     
     
    JavaScript的调试(Firebug Lite)
     
    Web开发工具整理:
    http://www.cnblogs.com/xjyggd/archive/2010/02/24/1672457.html
     
    实战: Firebug Lite (IE下如何使用FireBugLite)
    1、在线调用
    2、引用网上路径
    <script type="text/javascript" src="https://getfirebug.com/firebug-lite.js"></script>
    3、引用本地路径
  • 相关阅读:
    0317复利计算的回顾与总结
    0518 Scrum 项目 5.0
    0517 Scrum 项目4.0
    0512 Scrum 项目3.0
    实验三 进程调度模拟程序
    0505 Scrum 项目1.0
    0502团队项目 SCRUM团队成立
    0428 团队项目2.0
    0422团队项目
    实验二 作业调度模拟程序
  • 原文地址:https://www.cnblogs.com/sunliyuan/p/5866805.html
Copyright © 2020-2023  润新知