DOM 无关事件
基于事件的编程非常强大,因为它能让你的应用架构充分解耦,让功能变得更加内聚且具有更好的可维护性。事件本质上是和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 = {});
- // 针对给定的事件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 请求结束时的回调等场景。