JS 事件框架用于 OO 架构






3.67/5 (2投票s)
一个适用于所有JavaScript需求的、可重用的事件框架。
引言
本文将介绍我开发的事件框架。它允许我以更传统的面向对象方式进行JavaScript开发。
背景
在HTML 4中作为客户端编辑器时,我迫切需要一种创建事件的简单方法,该方法与其他语言类似,即可以向事件注册多个委托实例,并在事件触发时调用所有实例。
JavaScript中的方法调用
许多语言都具有反射库,以便充分利用面向对象语言的多态性。JavaScript没有反射库,但你可以控制方法运行的上下文来调用方法。
该过程使用函数的“call
”方法进行。以下示例显示了“call
”方法的简单实现
var fTest = function() { alert('test'); }
fTest.call();
这将调用fTest
方法并显示警报框。
你还可以通过“call
”在调用方法时提供上下文。这里的上下文指的是方法体内的对象引用作为“this
”。考虑以下示例
var fTest = function(text) { alert(this.Name + ': ' + text); }
var context = { Name: 'dave' };
fTest.call(context, 'forename');
生成的警报将显示“dave: forename”。由于context
JSON对象作为方法调用的“this
”引用,因此this.Name
返回“dave”。后续参数传递到“call
”方法并作为常规参数传递。
代码
代码的第一部分通过扩展Array
对象,添加Add
、RemoveAt
和IndexOf
方法来改进JavaScript中的Array
对象。
在事件框架中,数组对象用于存储已注册操作的列表。
Array.prototype.Add = function(obj) { this[this.length] = obj; }
Array.prototype.RemoveAt = function(index) {
var result = new Array();
var offset = 0;
for (var x = 0; x < this.length; x++) {
if (x != index)
result[result.length] = this[x];
}
return result;
}
Array.prototype.IndexOf = function(value) {
for (var x = 0; x < this.length; x++)
if (this[x] == value)
return x;
return -1;
}
下一部分定义了uiEvent
对象。这是实例化以创建新事件的对象。本文档稍后将提供一个实现示例。
var uiEventRegistration = function() {
this.ID = null;
this.Method = null;
this.Caller = null;
}
var uiEvent = function() {
this.TimerEvent = false;
this.__registered = new Array();
this.__currentID = 0;
this.Register = function(func, caller) {
var reg = new uiEventRegistration();
reg.ID = this.__currentID;
reg.Method = func;
reg.Caller = caller;
this.__registered.Add(reg);
var returnID = this.__currentID;
this.__currentID++;
return returnID;
}
this.Deregister = function(RegistrationID) {
var index = -1;
for (var x = 0; x < this.__registered.length; x++)
if (this.__registered[x].ID == RegistrationID) {
index = x;
break;
}
if (index != -1) {
this.__registered = this.__registered.RemoveAt(index);
}
}
this.Fire = function() {
if (!this.TimerEvent)
uiTimerExecute();
var a = arguments;
for (var x = 0; x < this.__registered.length; x++) {
this.__registered[x].Method.call(this.__registered[x].Caller,
a[0] == null ? null : a[0],
a[1] == null ? null : a[1],
a[2] == null ? null : a[2],
a[3] == null ? null : a[3],
a[4] == null ? null : a[4],
a[5] == null ? null : a[5],
a[6] == null ? null : a[6],
a[7] == null ? null : a[7],
a[8] == null ? null : a[8],
a[9] == null ? null : a[9])
}
}
}
现在我们有一个名为uiEvent
的类,可用于在JavaScript中创建传统事件。以下示例是框架的基本实现。
var eventTest = new uiEvent();
var context1 = { Name: 'Dave' };
var context2 = { Name: 'Chris' };
function delegate1(mood) {
alert(this.Name + ' is ' + mood);
}
function delegate2(mood) {
alert(mood + ' is not a good thing for ' + this.Name);
}
eventTest.Register(delegate1, context1);
eventTest.Register(delegate2, context2);
eventTest.Fire('sad');
在上面的示例中,将出现两个警报。第一个会说“Dave is sad”。第二个会说“sad is not a good thing for Chris”。
不公平的优先级
在IE中,UI事件(例如onmousemove
和onkeypress
)比从setTimeout
或setInterval
执行的调用具有更高的优先级。这在开发动画框架时(该框架将在后续文章中进一步讨论)造成了一个问题。
当我构建一个拖放编辑器时,大量代码链接到onmousemove
事件。这并不总是会在下一个onmousemove
事件在JavaScript引擎中注册之前完成。这导致setInterval
调用被阻止,直到鼠标保持静止。
为了反转优先级并首先强制基于计时器的事件触发,我已在事件框架中包含以下类
var uiTimers = new Array();
var uiTimerCount = 0;
function uiTimerExecute(timerID) {
for(var x = 0; x < uiTimers.length; x++)
{
if (timerID == null || uiTimers[x].TimerID == timerID) {
if (uiTimers[x].Expired()) {
uiTimers[x].OnTick.Fire();
var d = new Date();
uiTimers[x].NextTime = new Date(d.getYear(), d.getMonth(), d.getDate(),
d.getHours(), d.getMinutes(), d.getSeconds(),
d.getMilliseconds() + (uiTimers[x].Interval));
}
}
}
}
var uiTimer = function(interval) {
uiTimers.Add(this);
this.Interval = interval;
this.StartDate = new Date();
this.NextTime = new Date(this.StartDate.getYear(), this.StartDate.getMonth(),
this.StartDate.getDate(), this.StartDate.getHours(),
this.StartDate.getMinutes(), this.StartDate.getSeconds(),
this.StartDate.getMilliseconds() + (interval));
this.OnTick = new uiEvent();
this.OnTick.TimerEvent = true;
this.Expired = function() { return this.NextTime < new Date(); };
this.TimerID = uiTimerCount;
setInterval('uiTimerExecute(' + this.TimerID + ');', interval);
}
uiTimer
类将跟踪计划的间隔是否已过期,并以高于正常优先级的优先级引发事件,因为事件框架会检查它是否已过期,而不是等待setInterval
评估运行。
以下是uiTimer
的实现示例
var secondTicks = 0;
function secondIncrement() {
secondTicks++;
window.status = secondTicks;
}
var secondTimer = new uiTimer(1000);
secondTimer.OnTick.Register(secondIncrement);