首页 > 代码库 > Javascript事件设计模式(七)

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="http://www.mamicode.com/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="http://www.mamicode.com/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="http://www.mamicode.com/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://sourceforge.net/project/showfiles.php?group_id=175776
 
在线压缩:http://www.xidea.org/project/jsa/
 
JSA的特点:
        相比之下JSA的是当前比较安全最有效的压缩工具。
JSA 不仅提供代码压缩功能,还可以做格式化,脚本分析。
脚本分析功能可以用于查看脚本信息,以及查找脚本中的潜在问题。
比如查看脚本中申明了那些函数,变量。
使用了那些外部变量。等等。。。
JSA的压缩过程分两步,第一步是语法压缩,安全且有效。
第二步是文本压缩,目前采用的是
JavaScript Compressor的压缩算法。
(http://dean.edwards.name/packer/)
这些都可以在设置窗口设置。
默认情况先用语法压缩,当文件大于1000k且采用文本压缩仍然可以压缩到原来大小90%时才在原来基础上采用文本压缩。
详见:Google Closure Compiler压缩优化规则初探
 
开启服务端的HTTP压缩功能(IIS)
 开启服务端的HTTP压缩功能(IIS)
 
 详见:IIS上启用Gzip压缩(HTTP压缩) 详解.pdf
 
JavaScript的调试(Firebug Lite)
下载地址:http://getfirebug.com/firebuglite#Install
 
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、引用本地路径
http://getfirebug.com/firebuglite#Stable

Javascript事件设计模式(七)