javascript事件模型代码_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 月影

时间: 2024-08-01 21:51:22

javascript事件模型代码_javascript技巧的相关文章

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

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

Javascript处理DOM元素事件实现代码_javascript技巧

DOM元素都有一些标准事件,一般使用时只要使用onclick=function的方式就可以了,但是当需要为DOM元素添加多个事件,删除事件,或在用Javascript封装控件的时候,为封装的控件添加自定义事件的时候,onclick=function的方式就不够用了,但是浏览器有addEventListener和attachEvent方法可供调用,从而模拟出类似于C#中的事件委托的事件触发机制! 复制代码 代码如下: /* * 功能:事件处理 * Author:LQB * 时间:2009-1-4

兼容各大浏览器的JavaScript阻止事件冒泡代码_javascript技巧

这里仅仅是一个简单代码demo,因为时间问题并未做深入研究,因为今天做项目时要用到阻止事件冒泡的内容,找了好多才找到一个可以使用的,特记录之. <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>阻止事件冒泡</title> <scr

js enter键激发事件实例代码_javascript技巧

如下所示: document.onkeydown = function (e) { if (!e) e = window.event; if ((e.keyCode || e.which) == 13) { $("#btnSubmit").click(); } } 以上这篇js enter键激发事件实例代码就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持. 以上是小编为您精心准备的的内容,在的博客.问答.公众号.人物.课程等栏目也有的相关内容,欢迎继续使用右上角

JavaScript事件用法浅析_javascript技巧

本文实例讲述了JavaScript事件用法.分享给大家供大家参考,具体如下: JavaScript通过事件与HTML交互. 事件流 事件流规定了事件的触发规则和顺序.DOM2规定了事件流包括三个阶段:事件捕获 -> 目标触发除 -> 事件冒泡.DOM2规定在事件捕获阶段不应调用事件处理程序,不过各大浏览器都不鸟它.DOM2级的事件处理程序操作函数对:addEventListener和removeEventListener的第三个参数则把这种事变成了DIY,这是一种妥协,同时让初学者认为dom的

Google Map V3 绑定气泡窗口(infowindow)Dom事件实现代码_javascript技巧

在调试功能模块时候,发现怎么用什么方法都无法在infowindow里面添加的div进行绑定事件处理.郁闷啊!上网搜了好多方法也没用, 后来想想还是查了一下官方的API,发现了google.maps.InfoWindow下面的Events里面有个domready事件 官方解释: This event is fired when the containing the InfoWindow's content is attached to the DOM. You may wish to monito

浅析JavaScript事件和方法_javascript技巧

单击一个超链接触发事件 1.用a标签的onclick <a href="#" onclick="js代码"> 这种写法呢,存在一种弊端,就是点击后会刷新一次页面,回到页面顶端. 想不刷新如何做的呢? 复制代码 代码如下: <a href="javascript:void(0)" onclick="js代码"> 2.#与javascript:void(0)的区别 超链接为"死链"时,使

js和jquery实现监听键盘事件示例代码_javascript技巧

项目中要监听键盘组合键CTRL+C,以便做出对应的响应.查了一些方法但是其兼容性和稳定性不是很高,最终得到如下方法,经测试在Firfox.Chrome.IE中均可以使用.一.使用javascript实现 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script> function keyListener(ev

两个比较有用的Javascript工具函数代码_javascript技巧

1.大家在实际工作中,会写各式各样的赋值语句. 比如最常用的obj.style.display = "none"; 如果这样的赋值语句一多,obj.style一排下来都要看晕了 下面我的base.js中的extend函数可以允许用json格式赋值属性甚至是函数句柄 复制代码 代码如下: /** * 扩展函数 * @param target 需要扩展的对象 * @param params 要往target里放的属性和方法 */ function extend(target, params