DOM无关事件(节选自基于MVC的js富客户端应用)
基于事件的编程非常强大,因为它能让你的应用架构充分解耦,让功能变得更加内聚且
具有更好的可维护性。事件本质上是和DOM无关的,因此你可以很容易开发出一个事
件驱动的库。这种模式称为发布/订阅(http://en.wikipedia.org/wiki/Publish/subscribe),
这是一个很有用的模式。
发布/订阅(Pub/Sub)是一种消息模式,它有两个参与者:发布者和订阅者。发布者向
某个信道(channel)发布一条消息,订阅者绑定这个信道,当有消息发布至信道时就会
接收到一个通知。最重要的一点是,发布者和订阅者是完全解耦的,彼此并不知晓对方
的存在。两者仅仅共享一个信道名称。
发布者和订阅者的解耦可以让你的应用易于扩展,而不必引入额外的交叉依赖和耦合,
从而提高了应用的可维护性,添加额外功能也非常容易。
那么,应当如何在应用中使用发布/订阅(Pub/Sub)模式呢?你只需记录回调和事件名
称的对应关系及调用它们的方法。看一下这个例子,这段代码中实现了PubSub对象,用
它可以添加并触发事件监听:
var PubSub = { subscribe: function(ev, callback) { // 创建 _callbacks 对象,除非它已经存在了 var calls = this._callbacks || (this._callbacks = {}); 28 | 31 // 针对给定的事件key创建一个数组,除非这个数组已经存在 // 然后将回调函数追加到这个数组中 (this._callbacks[ev] || (this._callbacks[ev] = [])).push(callback); return this; }, publish: function() { // 将arguments对象转换为真正的数组 var args = Array.prototype.slice.call(arguments, 0); // 拿出第1个参数,即事件名称 var ev = args.shift(); // 如果不存在_callbacks对象,则返回 // 或者如果不包含给定事件对应的数组 var list, calls, i, l; if (!(calls = this._callbacks)) return this; if (!(list = this._callbacks[ev])) return this; // 触发回调 for (i = 0, l = list.length; i < l; i++) list[i].apply(this, args); return this; } }; // 使用方法 PubSub.subscribe("wem", function(){ alert("Wem!"); }); PubSub.publish("wem");
你可以使用命名空间的方式来管理事件名称,比如使用冒号分隔符(:)。
PubSub.subscribe("user:create", function(){ /* ... */ });
如果你在使用jQuery,可以关注下Ben Alman(http://benalman.com)写的一个更容易
的库(http://gist.github.com/799721/c119783954e1b10551c4afef53b2c04fefcb7465)。这个
库非常简单,用不了一页纸:
/*! * jQuery Tiny Pub/Sub - v0.3 - 11/4/2010 * http://benalman.com/ * * Copyright (c) 2010 "Cowboy" Ben Alman * Dual licensed under the MIT and GPL licenses. * http://benalman.com/about/license/ (function($){ var o = $({}); $.subscribe = function() { o.bind.apply( o, arguments ); }; $.unsubscribe = function() { o.unbind.apply( o, arguments ); }; $.publish = function() { o.trigger.apply( o, arguments ); }; })(jQuery);
这里的API和jQuery的bind()及trigger()函数的参数一致。惟一的区别就是这两个
函数直接保存在jQuery对象中,且名叫publish()和subscribe():
$.subscribe( "/some/topic", function( event, a, b, c ) {
console.log( event.type, a + b + c );
});
$.publish( "/some/topic", "a", "b", "c" );
上面我们将Pub/Sub用于全局事件,也能很容易地将其用于局部事件。现在我们用上面
提到的PubSub对象来创建一个对象的局部事件:
var Asset = {};
// 添加PubSub
jQuery.extend(Asset, PubSub);
// 现在就可以用publish/subscribe函数了
Asset.subscribe("create", function(){
// ...
});
我们使用了jQuery的extend()来将PubSub的属性复制至Asset对象。这样的话,所有
对publish()和subscribe()的调用均是针对Asset的局部调用。这在很多场景中非常
有用,包括对象关系映射(ORM)中的事件、状态机及当Ajax请求结束时的回调等场景

浙公网安备 33010602011771号