首页 > 代码库 > 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的压缩和混淆技术:
一:
优点:简单易用
JSA 不仅提供代码压缩功能,还可以做格式化,脚本分析。
脚本分析功能可以用于查看脚本信息,以及查找脚本中的潜在问题。
比如查看脚本中申明了那些函数,变量。
使用了那些外部变量。等等。。。
JSA的压缩过程分两步,第一步是语法压缩,安全且有效。
第二步是文本压缩,目前采用的是
JavaScript Compressor的压缩算法。
(http://dean.edwards.name/packer/)
这些都可以在设置窗口设置。
默认情况先用语法压缩,当文件大于1000k且采用文本压缩仍然可以压缩到原来大小90%时才在原来基础上采用文本压缩。
Javascript事件设计模式(七)