javascript 事件设计概述



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



最简单的事件设计模式

最简单的一种模式是将一个类的方法成员定义为事件,这不需要任何特殊的语法,通常是一个空方法,例如:

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 objOnShow(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封装,就可以用一种通用的方案实现参数传递了。这不仅适用于自定义的事件,也适用于系统提供的事件,其原理是完全相同的。





使自定义事件支持多绑定

可以用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.οnshοw=[]; //用数组存储绑定的事件处理程序引用

            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方法用于取消绑定。这些实现的基本思想都是对数组的操作。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值