|
1 事件處理模式
在程序設(shè)計(jì)領(lǐng)域,“事件處理”是一種模式,當(dāng)一個(gè)對(duì)象受外部影響而改變狀態(tài)時(shí),通過(guò)消息的方式將這個(gè)狀態(tài)改變通知給這個(gè)對(duì)象或者相關(guān)聯(lián)的某個(gè)對(duì)象,讓它執(zhí)行對(duì)應(yīng)的動(dòng)作,這就是事件處理的基本原理。負(fù)責(zé)通知狀態(tài)改變的對(duì)象被稱作“消息”,而執(zhí)行響應(yīng)動(dòng)作的屬性則被稱作“事件代理”。
例如下面就是一個(gè)簡(jiǎn)單的事件處理模式的應(yīng)用:
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);
在上面的例子中,函數(shù)dispatchEvent負(fù)責(zé)分派事件,onsignalchange是事件代理,在這個(gè)差分系統(tǒng)diff10中,當(dāng)輸入信號(hào)的電平發(fā)生變化(從0到1或者從1到0)時(shí),觸發(fā)相應(yīng)的事件onsignalchange,并且將當(dāng)前輸入信號(hào)、時(shí)序和當(dāng)前輸出緩存作為事件參數(shù)傳入事件處理程序。
diff10.onsignalchange = function(eventArgs)
{
alert(eventArgs.time);
}
是程序員指定的事件處理程序,在這里我們打印出輸入電平發(fā)生變化時(shí)的輸入信號(hào)時(shí)序。
2 用戶事件接口的定義
前面的例子中,我們僅僅定義了一個(gè)用來(lái)分派事件的函數(shù)dispatchEvent,但它也可以看作是一個(gè)完整的用戶事件接口,現(xiàn)在我們回顧這個(gè)函數(shù),弄明白它究竟做了什么樣的事情:
function dispatchEvent(owner, eventName, eventArgs)
{
if(owner && owner["on"+eventName])
setTimeout(function(){owner["on"+eventName](eventArgs)}, 1);
}
這個(gè)函數(shù)接收三個(gè)參數(shù),它的第一個(gè)參數(shù)是一個(gè)對(duì)象,指定了這個(gè)事件的“所有者”,即這個(gè)事件是由誰(shuí)接收和負(fù)責(zé)處理的。在上面的例子中,這個(gè)owner是Differ對(duì)象本身即
dispatchEvent(this, "signalchange", {input:serials, time:time, buffer:buffer});
傳入的owner參數(shù)是this,實(shí)際上事件模式允許其他類型作為事件分派的所有者,尤其在一些特定的模式,通常事件的發(fā)起者和事件的接收者可以不是同一個(gè)對(duì)象。在4小節(jié)介紹的觀察者模式中可以看到這一點(diǎn)。
第二個(gè)參數(shù)是一個(gè)表示事件類型的字符串,它決定了事件代理的名稱,根據(jù)事件模型的規(guī)范,事件代理的名稱為”on”+事件類型,例如上面例子中,事件類型為signalchange,對(duì)應(yīng)的事件代理為onsignalchange。
第三個(gè)參數(shù)是一個(gè)事件參數(shù)對(duì)象,它決定了傳遞給事件接收者的參數(shù),在上面的例子中,它傳遞了input、time和buffer三個(gè)屬性,分別代表發(fā)生事件時(shí)的當(dāng)前輸入序列、時(shí)序以及輸出緩存的值。
dispatchEvent函數(shù)本身的內(nèi)容很簡(jiǎn)單,它只是確保調(diào)用接收者的事件代理,并將事件參數(shù)正確傳入這個(gè)事件代理。至于事件代理是如何處理事件參數(shù)的,它并不關(guān)心。
3 事件代理和事件注冊(cè)
在事件處理模式中,為事件代理指定事件處理函數(shù)的過(guò)程被稱為事件注冊(cè)。在上面的例子中,diff10.onsignalchange是極其簡(jiǎn)單的事件代理,它的事件注冊(cè)過(guò)程也極為簡(jiǎn)單――采用直接賦值的方式來(lái)完成。
事實(shí)上根據(jù)設(shè)計(jì)的不同,事件代理可以有更加復(fù)雜的注冊(cè)方式,例如DOM-level-2的addEventListener和removeEventListener,我們也可以實(shí)現(xiàn)類似的事件注冊(cè)方法,以支持為一個(gè)事件代理注冊(cè)多個(gè)事件事件處理方法。為了實(shí)現(xiàn)它,我們完善事件接口,修改上面的例子如下:
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);
在上面的例子里,我們建立了一個(gè)EventManager類型,為它定義了三個(gè)對(duì)象方法,dispatchEvent方法和前面那個(gè)例子很類似,是用來(lái)分派事件的,而另外的addEventListener和removeEventListener則是用來(lái)注冊(cè)和注銷事件處理函數(shù)。
在Differ類型中,我們通過(guò)EventManager.call(this);將EventManager類型的實(shí)例運(yùn)用到Differ原型中(關(guān)于這個(gè)問(wèn)題的深層機(jī)制,留待以后再進(jìn)行詳細(xì)討論)。然后調(diào)用this.dispatchEvent來(lái)分派事件。
在為Differ實(shí)例的onsignalchange事件代理注冊(cè)事件時(shí),你會(huì)發(fā)現(xiàn)它和標(biāo)準(zhǔn)的DOM事件模型非常類似:
diff10.addEventListener("signalchange",eventHandler1);
diff10.addEventListener("signalchange",eventHandler2);
diff10.removeEventListener("signalchange",eventHandler1);
運(yùn)行過(guò)這個(gè)例子,你會(huì)發(fā)現(xiàn)一個(gè)有趣的地方,就是diff10.input(inputSerials);觸發(fā)的事件并沒(méi)有執(zhí)行eventHandler1和eventHandler2,而是只執(zhí)行了eventHandler2,原因是:
diff10.removeEventListener("signalchange",eventHandler1);
先于事件的觸發(fā)被執(zhí)行,這是因?yàn)槭录C(jī)制是一種“異步回調(diào)”機(jī)制,關(guān)于同步和異步的問(wèn)題,我們以后討論。
4 標(biāo)準(zhǔn)模式:事件分派和接收
在事件處理模式中,事件的分派者負(fù)責(zé)發(fā)出消息,事件的接收者負(fù)責(zé)處理消息。在前面的例子里,它們是由同一個(gè)對(duì)象(Differ)完成的。
然而,事實(shí)上,事件處理模式中,并不要求消息的發(fā)送和接收由同一個(gè)對(duì)象完成,在某些模式中,它們是不同的對(duì)象,其中最常見(jiàn)的一種是“觀察者”模式,下面將差分系統(tǒng)的例子改寫為觀察者模式:
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類型,所以事件注冊(cè)的代理是DifferObserver的屬性,在發(fā)送的事件參數(shù)中,我們?cè)黾恿艘粋€(gè)屬性sender,它引用事件的實(shí)際發(fā)送對(duì)象
原文:http://bbs.51js.com/thread-69808-1-1.html by 月影
JavaScript技術(shù):javascript事件模型代碼,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。