惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
I
InfoQ
U
Unit 42
WordPress大学
WordPress大学
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
J
Java Code Geeks
月光博客
月光博客
D
Docker
Stack Overflow Blog
Stack Overflow Blog
D
DataBreaches.Net
阮一峰的网络日志
阮一峰的网络日志
Blog — PlanetScale
Blog — PlanetScale
V
Visual Studio Blog
博客园 - 聂微东
A
About on SuperTechFans
腾讯CDC
Jina AI
Jina AI
Microsoft Azure Blog
Microsoft Azure Blog
GbyAI
GbyAI
博客园 - 【当耐特】
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
M
MIT News - Artificial intelligence

博客园 - 想那风霜雪

基于web的甘特图,易度甘特图edogantt! JavaScript异常处理 - 想那风霜雪 - 博客园 一个支持Drag页面拖动的组件 - 想那风霜雪 - 博客园 main Function和Object(2) Function和Object(1) - 想那风霜雪 - 博客园 Atlas系列:从AtlasRuntime.js开始-简介 Atlas源码阅读系列之一 9)使用面向对象思想处理cookie 7)实现抽象类 6)类的继承 5)使用for(...in...)实现反射机制 4)公有成员\私有成员和静态成员 - 想那风霜雪 - 博客园 3)类的实现 2)深入认识JavaScript中的函数 1)JavaScript面向对象的基础 JavaScript的OO特性:Singleton单件模式 JavaScript的OO特性:私有访问限制实现 JavaScript的OO特性:继承
8)事件设计模式
想那风霜雪 · 2006-08-17 · via 博客园 - 想那风霜雪

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

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中被调用,这就实现了事件机制.
    上面的方法比较简单,但有如下缺点:
1>不能给事件处理程序传递参数.因为是在show()这个内部方法中调用事件处理程序的,无法知道外部的参数;
2>每个事件接口仅能绑定一个事件处理程序,而内部方法则可以使用attachEvent或者addEventListener方法绑定多个处理程序.

    2)给事件处理程序传递参数


    给事件处理程序传递参数不仅是自定义事件中存在的问题,也是系统内部对象的事件机制中存在的问题,因为事件机制仅传递一个函数的名称,不带有任何参数的信息,所以无法传递参数进入.例如:

//定义类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,下面是一个应用示例:

//将有参数的函数封装为无参数的函数
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();

    这里,就将变量userName作为传递传递给了objOnShow事件处理程序.事实上,obj.onShow得到的事件处理程序并不是objOnShow,而是由createFunction返回的一个无参函数.
   
通过createFunction封装,就可以用一种通用的方案实现参数传递了。这不仅适用于自定义的事件,也适用于系统提供的事件,其原理是完全相同的。
   
    4)使自定义事件支持多绑定
   
可以用attachEvent或者addEventListener方法来实现多个事件处理程序的同时绑定,不会互相冲 突,而自定义事件怎样来实现多订阅呢?下面介绍这种实现。要实现多订阅,必定需要一个机制用于存储绑定的多个事件处理程序,在事件发生时同时调用这些事件 处理程序。从而达到多订阅的效果,其实现如下:

<script language="JavaScript" type="text/javascript">
//定义类class1
function class1(){
      
//构造函数
}
//定义类成员
class1.prototype={
      show:
function(){          //触发事件
           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方法用于取消绑定。这些实现 的基本思想都是对数组的操作。

posted on 2006-08-17 08:01  想那风霜雪  阅读(633)  评论()    收藏  举报