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

推荐订阅源

Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
腾讯CDC
D
Docker
G
Google Developers Blog
D
DataBreaches.Net
雷峰网
雷峰网
Blog — PlanetScale
Blog — PlanetScale
S
SegmentFault 最新的问题
The Cloudflare Blog
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
量子位
美团技术团队
aimingoo的专栏
aimingoo的专栏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Engineering at Meta
Engineering at Meta
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 想那风霜雪

基于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)  评论()    收藏  举报