javascript事件模型代码

本节稍稍深入地讨论关于事件处理的话题,如果你对模式、闭包和面向对象等概念还不太理解,不妨暂且等阅读完相关内容之后再回过头来阅读它,相信你会有很大收获。

1 事件处理模式

在程序设计领域,“事件处理”是一种模式,当一个对象受外部影响而改变状态时,通过消息的方式将这个状态改变通知给这个对象或者相关联的某个对象,让它执行对应的动作,这就是事件处理的基本原理。负责通知状态改变的对象被称作“消息”,而执行响应动作的属性则被称作“事件代理”。
       例如下面就是一个简单的事件处理模式的应用:

function dispatchEvent(owner, eventType, eventArgs)
{
 if(owner && owner["on"+eventType])
   setTimeout(function(){owner["on"+eventType](eventArgs)}, 1);
}

function randomSerials(len)
{
 function randomSignal()
 {
   return Math.random() > 0.5 ? 1 : 0;
 }
 var ret = [];
 for(var i = 0; i < len; i++)
 {
   ret.push(randomSignal());
 }
 return ret;
}

function Differ(obl)
{
 var buffer = new Array(obl);
 var time = 0;

this.readBuffer = function()
 {
   var buf = buffer;

buffer = new Array(obl);
   time = 0;

return buf;
 }

this.bufferSize = function()
 {
   return obl;
 }

this.input = function(serials)
 {
   for(var i = 1; i < serials.length; i++)
   {
       var signal = Math.abs(serials[i] - serials[i - 1]);
       buffer[time++ % obl] = signal;
       if(signal) 
         dispatchEvent(this, "signalchange", 
{input:serials, time:time, buffer:buffer.slice(0)});
   }
 }
}

var inputSerials = randomSerials(20);
alert(inputSerials);
var diff10 = new Differ(20);
diff10.input(inputSerials);
alert(diff10.readBuffer());

diff10.onsignalchange = function(eventArgs)
{
 alert(eventArgs.time);
}

diff10.input(inputSerials);

在上面的例子中,函数dispatchEvent负责分派事件,onsignalchange是事件代理,在这个差分系统diff10中,当输入信号的电平发生变化(从0到1或者从1到0)时,触发相应的事件onsignalchange,并且将当前输入信号、时序和当前输出缓存作为事件参数传入事件处理程序。

diff10.onsignalchange = function(eventArgs)
{
       alert(eventArgs.time);
}

是程序员指定的事件处理程序,在这里我们打印出输入电平发生变化时的输入信号时序。

2 用户事件接口的定义

前面的例子中,我们仅仅定义了一个用来分派事件的函数dispatchEvent,但它也可以看作是一个完整的用户事件接口,现在我们回顾这个函数,弄明白它究竟做了什么样的事情:

function dispatchEvent(owner, eventName, eventArgs)
{
       if(owner && owner["on"+eventName])
               setTimeout(function(){owner["on"+eventName](eventArgs)}, 1);
}

这个函数接收三个参数,它的第一个参数是一个对象,指定了这个事件的“所有者”,即这个事件是由谁接收和负责处理的。在上面的例子中,这个owner是Differ对象本身即
dispatchEvent(this, "signalchange", {input:serials, time:time, buffer:buffer});
       传入的owner参数是this,实际上事件模式允许其他类型作为事件分派的所有者,尤其在一些特定的模式,通常事件的发起者和事件的接收者可以不是同一个对象。在4小节介绍的观察者模式中可以看到这一点。
       第二个参数是一个表示事件类型的字符串,它决定了事件代理的名称,根据事件模型的规范,事件代理的名称为”on”+事件类型,例如上面例子中,事件类型为signalchange,对应的事件代理为onsignalchange。
       第三个参数是一个事件参数对象,它决定了传递给事件接收者的参数,在上面的例子中,它传递了input、time和buffer三个属性,分别代表发生事件时的当前输入序列、时序以及输出缓存的值。
       dispatchEvent函数本身的内容很简单,它只是确保调用接收者的事件代理,并将事件参数正确传入这个事件代理。至于事件代理是如何处理事件参数的,它并不关心。

3 事件代理和事件注册

在事件处理模式中,为事件代理指定事件处理函数的过程被称为事件注册。在上面的例子中,diff10.onsignalchange是极其简单的事件代理,它的事件注册过程也极为简单——采用直接赋值的方式来完成。
事实上根据设计的不同,事件代理可以有更加复杂的注册方式,例如DOM-level-2的addEventListener和removeEventListener,我们也可以实现类似的事件注册方法,以支持为一个事件代理注册多个事件事件处理方法。为了实现它,我们完善事件接口,修改上面的例子如下:

function EventManager(owner)
{
 owner = owner || this;

this.dispatchEvent = function(eventType, eventArgs)
 {
   var events = owner["on"+eventType];
   if(events && typeof(events) == "function")
     events = [events];
   if(owner && events)
   {
     for(var i = 0; i < events.length; i++)
     {
       setTimeout(
         (function(i){return  function(){events[i](eventArgs)}
         })(i), 1
       );
     }
   }
 }

this.addEventListener = function(eventType, closure)
 {
   if(owner["on"+eventType] == null)
   {
     owner["on"+eventType] = [];
   }
   var events = owner["on"+eventType];
   if(events && typeof(events) == "function")
     events = [events];    
   events.push(closure);
 }

this.removeEventListener = function(eventType, closure)
 {
   var events = owner["on"+eventType];
   if(events && typeof(events) == "function")
     events = [events];

for(var i = 0; i < events.length; i++)
   {
     if(events[i] == closure)
       events.splice(i, 1);
   }
 }
}

function randomSerials(len)
{
 function randomSignal()
 {
   return Math.random() > 0.5 ? 1 : 0;
 }
 var ret = [];
 for(var i = 0; i < len; i++)
 {
   ret.push(randomSignal());
 }
 return ret;
}

function Differ(obl)
{
 var buffer = new Array(obl);
 var time = 0;

EventManager.call(this);  //apply EnventManager Component.

this.readBuffer = function()
 {
   var buf = buffer;

buffer = new Array(obl);
   time = 0;

return buf;
 }

this.bufferSize = function()
 {
   return obl;
 }

this.input = function(serials)
 {
   for(var i = 1; i < serials.length; i++)
   {
       var signal = Math.abs(serials[i] - serials[i - 1]);
       buffer[time++ % obl] = signal;
       if(signal) 
         this.dispatchEvent("signalchange", 
{input:serials, time:time, buffer:buffer.slice(0)});
   }
 }
}

var inputSerials = randomSerials(20);
alert(inputSerials);
var diff10 = new Differ(20);
diff10.input(inputSerials);
alert(diff10.readBuffer());

var eventHandler1 = function(eventArgs){
 alert(eventArgs.time);
}

var eventHandler2 = function(eventArgs){
 alert(eventArgs.buffer);
}

diff10.addEventListener("signalchange",eventHandler1);
diff10.addEventListener("signalchange",eventHandler2);
diff10.input(inputSerials);

diff10.removeEventListener("signalchange",eventHandler1);

在上面的例子里,我们建立了一个EventManager类型,为它定义了三个对象方法,dispatchEvent方法和前面那个例子很类似,是用来分派事件的,而另外的addEventListener和removeEventListener则是用来注册和注销事件处理函数。
       在Differ类型中,我们通过EventManager.call(this);将EventManager类型的实例运用到Differ原型中(关于这个问题的深层机制,留待以后再进行详细讨论)。然后调用this.dispatchEvent来分派事件。
       在为Differ实例的onsignalchange事件代理注册事件时,你会发现它和标准的DOM事件模型非常类似:
diff10.addEventListener("signalchange",eventHandler1);
diff10.addEventListener("signalchange",eventHandler2);
diff10.removeEventListener("signalchange",eventHandler1);

运行过这个例子,你会发现一个有趣的地方,就是diff10.input(inputSerials);触发的事件并没有执行eventHandler1和eventHandler2,而是只执行了eventHandler2,原因是:
diff10.removeEventListener("signalchange",eventHandler1);
       先于事件的触发被执行,这是因为事件机制是一种“异步回调”机制,关于同步和异步的问题,我们以后讨论。

4 标准模式:事件分派和接收

在事件处理模式中,事件的分派者负责发出消息,事件的接收者负责处理消息。在前面的例子里,它们是由同一个对象(Differ)完成的。
       然而,事实上,事件处理模式中,并不要求消息的发送和接收由同一个对象完成,在某些模式中,它们是不同的对象,其中最常见的一种是“观察者”模式,下面将差分系统的例子改写为观察者模式:

function dispatchEvent(owner, eventType, eventArgs)
{
 if(owner && owner["on"+eventType])
   setTimeout(function(){owner["on"+eventType](eventArgs)}, 1);
}

function randomSerials(len)
{
 function randomSignal()
 {
   return Math.random() > 0.5 ? 1 : 0;
 }
 var ret = [];
 for(var i = 0; i < len; i++)
 {
   ret.push(randomSignal());
 }
 return ret;
}

function DifferObserver(differ)
{
 this.differ = differ;
 differ.setObserver(this);
}

function Differ(obl)
{
 var buffer = new Array(obl);
 var time = 0;
 var observer = null;

this.input = function(serials)
 {
   for(var i = 1; i < serials.length; i++)
   {
       var signal = Math.abs(serials[i] - serials[i - 1]);
       buffer[time++ % obl] = signal;
       if(signal) 
         dispatchEvent(observer, "signalchange", {sender:this, input:serials, time:time, buffer:buffer.slice(0)});
   }
 }

this.setObserver = function(obs)
 {
   observer = obs;
   observer.readBuffer = function()
   {
     var buf = buffer;

buffer = new Array(obl);
     time = 0;

return buf;
   }
   observer.bufferSize = function()
   {
     return obl;
   }
 }
}

var inputSerials = randomSerials(20);
alert(inputSerials);
var diff10 = new Differ(20);
diff10.input(inputSerials);
var diffObs = new DifferObserver(diff10);
alert(diffObs.readBuffer());

diffObs.onsignalchange = function(eventArgs)
{
 if(diff10 == eventArgs.sender)
   alert(eventArgs.time);
}

diff10.input(inputSerials);

上面例子中的事件分派者是Differ类型,而事件接收者则是DifferObserver类型,所以事件注册的代理是DifferObserver的属性,在发送的事件参数中,我们增加了一个属性sender,它引用事件的实际发送对象

原文:http://bbs.51js.com/thread-69808-1-1.html by 月影

(0)

相关推荐

  • 详解JavaScript中的自定义事件编写

    我们可以自定义事件来实现更灵活的开发,事件用好了可以是一件很强大的工具,基于事件的开发有很多优势(后面介绍). 与自定义事件的函数有 Event.CustomEvent 和 dispatchEvent. 直接自定义事件,使用 Event 构造函数: var event = new Event('build'); // Listen for the event. elem.addEventListener('build', function (e) { ... }, false); // Disp

  • javascript事件模型实例分析

    本文实例讲述了javascript事件模型的用法.分享给大家供大家参考.具体分析如下: 一.事件模型 冒泡型事件(Bubbling):事件由叶子节点沿祖先节点一直向上传递到根节点 捕获型事件(Capturing):由DOM树最顶元素一直到最精确的元素,与冒泡型事件相反 DOM标准事件模型:DOM标准既支持冒泡型事件,也支持捕获型事件,可以说是两者的结合体,首先是捕获型,接着冒泡传递 二.事件对象 在IE浏览器中事件对象是window的一个属性,在DOM标准中,event必须作为唯一的参数传给事件

  • javascript事件捕获机制【深入分析IE和DOM中的事件模型】

    本文实例分析了javascript事件捕获机制.分享给大家供大家参考,具体如下: 1.什么是事件冒泡? 在排序算法中,我们学过冒泡排序法,所谓冒泡就是让底层的东西浮出水面,对于事件冒泡也同样是如此, 下面我们来看一个例子来说明什么是事件冒泡. <div> <button>测试</button> </div> <script> $("div").bind("click",function(){alert(&q

  • 详解javascript实现自定义事件

    我们平时在操作dom时候经常会用到onclick,onmouseover等一系列浏览器特定行为的事件, 那么自定义事件,顾名思义,就是自己定义事件类型,自己定义事件处理函数,在合适的时候需要哪个事件类型,就去调用哪个处理程序 1.js所支持的浏览器默认事件 浏览器特定行为的事件,或者叫系统事件,js默认事件等等都行,大家知道我指的什么就行,下文我叫他js默认事件. js默认事件的事件绑定,事件移出等一系列操作,相信大家都有用到过,如: //DOM0级事件处理程序 var oDiv = docum

  • javascript 自定义事件初探

    还有,"通过事件机制,可以将类设计为独立的模块,通过事件对外通信,提高了程序的开发效率.".相信C#程序员对事件的好处是深有体会的.好了,Code is cheap.看代码: function class1() { // 最简单的事件设计模式 } class1.prototype = { show: function () { this .onShow(); }, onShow: function () { } } function test() { var obj = new cla

  • js事件模型与自定义事件实例解析

    JavaScript 一个最简单的事件模型,需要有事件绑定与触发,还有事件删除. var eventModel = { list: {}, bind: function () { var args = [].slice.call(arguments), type = args[0], handlers = args.slice(1); if (typeof type === 'string' && handlers.length > 0) { for (var i = 0; i &l

  • Javascript基础知识(一)核心基础语法与事件模型

    一.Javascript核心基础语法 1.Javascript是一门增加交互效果的编程语言,它最初由网景公司发明,最后提交给ECMA(欧洲计算机制造商协会),ECMA将Javascript标准化,其命名为Javascript. 2.Javascript是一门解释性语言,无需编译就可以直接在浏览器下运行. 3.Javascript的用途? 1.可以控制网页中所有元素,增加.删除.修改元素的属性. 2.可以在html中放入动态文本. 3.响应用户在使用网页时产生的事件. 4.校验用户输入的数据. 5

  • javascript事件模型介绍

    一.原始事件模型 其事件类型:分为"输入事件(如onclicki)"和"语义事件(如onsubmit)" 事件程序的注册可以以下几种方式: 1.JS代码作为HTML性质值 复制代码 代码如下: <input type="button" value="Press me" onclick="alert('thanks');" 2.事件处理程序作为JS属性 附注:文档中的每个HTML元素在文档树中都有一个相

  • javascript 传统事件模型构造的事件监听器实现代码

    代码如下: 复制代码 代码如下: var br={}; br.eventRouter = {}; br.eventRouter.addListen = function(el,eventType,func){ if(!el.eventObjs){ el.eventObjs = {}; } if(!el.eventObjs[eventType]){ el.eventObjs[eventType]=[]; br.eventRouter.bindListen(el,eventType); } el.e

  • JavaScript自定义事件介绍

    很多DOM对象都有原生的事件支持,向div就有click.mouseover等事件,事件机制可以为类的设计带来很大的灵活性,相信.net程序员深有体会.随着web技术发展,使用JavaScript自定义对象愈发频繁,让自己创建的对象也有事件机制,通过事件对外通信,能够极大提高开发效率. 简单的事件需求 事件并不是可有可无,在某些需求下是必需的.以一个很简单的需求为例,在web开发中Dialog很常见,每个Dialog都有一个关闭按钮,按钮对应Dialog的关闭方法,代码看起来大概是这样 复制代码

  • 关于javascript DOM事件模型的两件事

    事件捕捉(Event Capture)的实现问题 W3C DOM Level2的事件模型规范中,事件在DOM树中的传播过程(从根节点到目标节点)被分为了两个阶段:捕捉(Capture)和冒泡(Bubbling).下面这个图能大概的说明整个过程: (from W3C) 如果想创建一个捕捉事件,在支持W3C 事件模型的浏览器中,将addEventListener的第三个参数设为true就好了.例如: 复制代码 代码如下: document.getElementById('foo').addEvent

随机推荐