事件(或消息)是一种常用的软件设计模式,能够降低消息处理者和消息发布者的之间的耦合,比如J2EE里面的JMS规范。设计模式中的观察者模式(也叫发布/订阅模式),这对于javascript代码同样适用。之前写的JQuery相关博客中,详细介绍了JQuery的事件处理机制和特性,具体可以参考这个目录下的文章。
JQuery事件处理其实就是使用了发布/订阅模式,包括它提供的命名空间机制、自定义事件都非常的棒,但是JQuery事件处理有一个缺陷:JQuery事件都是和DOM元素相关的,但是很多时候我们并不需要DOM元素,只希望使用事件的发布/订阅这种机制。
代码1:如果某个DOM元素不存在,那么不能依靠它使用事件的发布和订阅。
// not-exsit不存在的dom元素,事件处理无效
$("not-exsit").bind("self-event",function(){alert(11);});
$("not-exsit").trigger("self-event");
代码2:JQuery并没有提供全局的bind/trigger,下面代码会报错。
// 报异常$.trigger is not a function
$.bind("self-event",function(){alert(11);});
$.trigger("self-event");
<script src="amplify.js"></script>
<script>
amplify.subscribe( "self-event", function(context){
console.log("priority=5,data="+JSON.stringify(context));
}, 5);
amplify.subscribe( "self-event", function(context){
console.log("priority=6,data="+JSON.stringify(context));
}, 6);
amplify.subscribe( "self-event", function(context){
console.log("priority=3,data="+JSON.stringify(context));
}, 3);
amplify.publish("self-event",{data:11});
</script>
这段代码可以正常的运行,通过AmplifyJS的使用方式可以看到,它刚好弥补了JQuery事件处理的不足。
下面附上AmplifyJS的源码amplify.core.js,可以看到源码非常的简短,也很容易看懂。
/*!
* Amplify Core 1.1.2
*
* Copyright 2011 - 2013 appendTo LLC. (http://appendto.com/team)
* Dual licensed under the MIT or GPL licenses.
* http://appendto.com/open-source-licenses
*
* http://amplifyjs.com
*/
(function( global, undefined ) {
var slice = [].slice,
subscriptions = {};
var amplify = global.amplify = {
publish: function( topic ) {
if ( typeof topic !== "string" ) {
throw new Error( "You must provide a valid topic to publish." );
}
var args = slice.call( arguments, 1 ),
topicSubscriptions,
subscription,
length,
i = 0,
ret;
if ( !subscriptions[ topic ] ) {
return true;
}
topicSubscriptions = subscriptions[ topic ].slice();
for ( length = topicSubscriptions.length; i < length; i++ ) {
subscription = topicSubscriptions[ i ];
ret = subscription.callback.apply( subscription.context, args );
if ( ret === false ) {
break;
}
}
return ret !== false;
},
subscribe: function( topic, context, callback, priority ) {
if ( typeof topic !== "string" ) {
throw new Error( "You must provide a valid topic to create a subscription." );
}
if ( arguments.length === 3 && typeof callback === "number" ) {
priority = callback;
callback = context;
context = null;
}
if ( arguments.length === 2 ) {
callback = context;
context = null;
}
priority = priority || 10;
var topicIndex = 0,
topics = topic.split( /\s/ ),
topicLength = topics.length,
added;
for ( ; topicIndex < topicLength; topicIndex++ ) {
topic = topics[ topicIndex ];
added = false;
if ( !subscriptions[ topic ] ) {
subscriptions[ topic ] = [];
}
var i = subscriptions[ topic ].length - 1,
subscriptionInfo = {
callback: callback,
context: context,
priority: priority
};
for ( ; i >= 0; i-- ) {
if ( subscriptions[ topic ][ i ].priority <= priority ) {
subscriptions[ topic ].splice( i + 1, 0, subscriptionInfo );
added = true;
break;
}
}
if ( !added ) {
subscriptions[ topic ].unshift( subscriptionInfo );
}
}
return callback;
},
unsubscribe: function( topic, context, callback ) {
if ( typeof topic !== "string" ) {
throw new Error( "You must provide a valid topic to remove a subscription." );
}
if ( arguments.length === 2 ) {
callback = context;
context = null;
}
if ( !subscriptions[ topic ] ) {
return;
}
var length = subscriptions[ topic ].length,
i = 0;
for ( ; i < length; i++ ) {
if ( subscriptions[ topic ][ i ].callback === callback ) {
if ( !context || subscriptions[ topic ][ i ].context === context ) {
subscriptions[ topic ].splice( i, 1 );
// Adjust counter and length for removed item
i--;
length--;
}
}
}
}
};
}( this ) );
更详细的API使用,可以参考下面这2篇文章:
Extending Your jQuery Application with Amplify.js