Appearance
自定义事件
本节目标:理解“自定义事件”的核心思路,并能把它用于实际开发或面试表达。
事件是JavaScript与浏览器交互的主要途径。事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术。对象可以发布事件,用来表示在该对象生命周期中某个有趣的时刻到了。然后其他对象可以观察该对象,等待这些有趣的时刻到来并通过运行代码来响应。
观察者模式由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使说观察者不存在。从另一方面来说,观察者知道主体并能注册事件的回调函数(事件处理程序)。涉及DoM上时,DOM元素便是主体,你的事件处理代码便是观者。
事件是与DOM交互的最常见的方式,但它们也可以用于非DoM代码中——通过实现自定义事件。自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。实现此功能的基本模式可以如下定义:
function EventTarget() {
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function (type, handler) {
if (typeof this.handlers[type] == "undefined") {
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire: function (event) {
if (!event.target) {
event.target = this;
}
if (this.handlers[event.type] instanceof Array) {
var handlers = this.handlers[event.type];
for (var i = 0, len = handlers.length; i < len; i++) {
handlers[i](event);
}
}
},
removeHandler: function (type, handler) {
if (this.handlers[type] instanceof Array) {
var handlers = this.handlers[type];
for (var i = 0, len = handlers.length; i < len; i++) {
if (handlers[i] === handler) {
break;
}
}
handlers.splice(i, 1);
}
}
};EventTarget类型有一个单独的属性handlers,用于储存事件处理程序。还有三个方法: addHandler(),用于注册给定类型事件的事件处理程序;fire()用于触发一个事件;removeHandler(),用于注销某个事件类型的事件处理程序。 如果要发一个事件,要调用fire()函数。该方法接受一个单独的参数,是一个至少包含type 属性的对象。fire()方法先给event对象设置一个target,如果它尚未被指定的话。然后它就 査找对应该事件类型的一组处理程序,调用各个函数,并给出event对象。因为这些都是自定义事件,所以event对象上还需要的额外信息由你自己决定。
EventTarget类型的自定义事件使用如下:
function handleMessage(event) {
alert("Message received: " + event.message);
}
// 创建一个对象
var target = new EventTarget();
// 添加一个事件处理程序
target.addHandler("message", handleMessage);
// 触发事件
target.fire({ type: "message", message: "Hello world!" });
// 删除事件处理程序
target.removeHandler("message", handleMessage);
// 再次,应没有处理程序
target.fire({ type: "message", message: "Hello world!" });
因为这种功能是封装在一种自定义类型中的,其他对象可以继承EventTarget并获得这个行为,如下例所示:
function Person(name, age) {
EventTarget.call(this);
this.name = name;
this.age = age;
}
inheritPrototype(Person, EventTarget);
Person.prototype.say = function (message) {
this.fire({ type: "message", message: message });
};
Person类型使用了寄生组合继承方法来继承EventTarget. 一旦调用了 say()
方法,便触发了事件,它包含了消息的细节。在某种类型的另外的方法中调用£ire()方法是很常见的,同时它通常不是公开调用的。这段代码可以照如下方式使用:
function handleMessage(event) {
alert(event.target.name + " says: " + event.message);
}
//创建新person
var person = new Person("Nicholas", 29);
//添加一个事件处理程序
person.addHandler("message", handleMessage);
// 在该对象上调用1个方法,它触发消息事件
person.say("Hi there.");当代码中存在多个部分在特定时刻相互交互的情况下,自定义事件就非常有用了。这时,如果每个对象都有对其他所有对象的引用,那么整个代码就会紧密耦合,同时维护也变得很困难,因为对某个对象的修改也会影响到其他对象。使用自定义事件有助于解耦相关对象,保持功能的隔绝。在很多情况中,触发事件的代码和监听事件的代码是完全分离的。
