观察者模式由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来 观察该主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正 常运作即使观察者不存在。从另一方面来说,观察者知道主体并能注册事件的回调函数(事件处理程序)。
下面写一下主题的代码:
function EventTarget () {
this.handles = {}
}
EventTarget.prototype = {
constructor: EventTarget ,
addHandler: function(type, handler){
if (typeof this.handles[type] == 'undefined') {
this.handles[type] = []
}
this.handles[type].push(handler)
},
fire: function (event) {
if(!event.target){
event.target = this
}
if(this.handles[event.type] instanceof Array){
var handlers = this.handles[event.type]
for (var i = 0; i < handlers.length; i++) {
handlers[i](event)
}
}
},
removeHandler: function (type, handler) {
if(this.handles[type] instanceof Array){
var handlers = this.handles[type]
for (var i = 0; i < handlers.length; i++) {
if (handlers[i] === handler) {
break
}
}
handlers.splice(i,1)
}
}
}
EventTarget 类型有一个单独的属性
handlers,用于储存事件处理程序。
还有三个方法:
- addHandler(),用于注册给定类型事件的事件处理程序;
- fire(),用于触发一个事件;
- removeHandler(),用于注销某个事件类型的事件处理程序。
addHandler()方法接受两个参数:事件类型和用于处理该事件的函数。当调用该方法时,会进行 一次检查,看看 handlers 属性中是否已经存在一个针对该事件类型的数组;如果没有,则创建一个新 的。然后使用 push()将该处理程序添加到数组的末尾。
如果要触发一个事件,要调用 fire()函数。该方法接受一个单独的参数,是一个至少包含 type 属性的对象。fire()方法先给 event 对象设置一个 target 属性,如果它尚未被指定的话。然后它就 查找对应该事件类型的一组处理程序,调用各个函数,并给出 event 对象。因为这些都是自定义事件, 所以 event 对象上还需要的额外信息由你自己决定。
removeHandler()方法是 addHandler()的辅助,它们接受的参数一样:事件的类型和事件处理 程序。这个方法搜索事件处理程序的数组找到要删除的处理程序的位置。如果找到了,则使用 break 操作符退出 for 循环。然后使用 splice()方法将该项目从数组中删除。
观察者和观察者订阅事件的代码:(使用 EventTarget 类型的自定义事件)
function handleMessage (event) {
console.log('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'})
在这段代码中,定义了 handleMessage()函数用于处理 message 事件。它接受 event 对象并输 出 message 属性。调用 target 对象的 addHandler()方法并传给"message"以及 handleMessage() 函数。在接下来的一行上,调用了 fire()函数,并传递了包含 2 个属性,即 type 和 message 的对象 直接量。它会调用 message 事件的事件处理程序,这样就会显示一个警告框(来自 handleMessage())。 然后删除了事件处理程序,这样即使事件再次触发,也不会显示任何警告框。