JS模仿AS3事件机制
2010-4-18
练习下自定义事件的实现,想让JS可以用AS3的语法使用自定义事件,实现很简单,因为没考虑复杂的情况吧。试过后发现有些地方还是模仿不了,最主要不一样的地方是:
1.侦听函数没法自动绑定那个函数所在的object,必须手动把object传进去,像这样:dispatcher.addEventListener(SampleDispatcher.ACTION, e.action, e);
最后还要传“e”这个obj进去,否则函数action里的this不指向e
2.使用的时候继承十分麻烦,还得小心翼翼,看demo就知道了。
//事件发送者类
var EventDispatcher = function() {
this.listeners = {};
}
EventDispatcher.prototype = {
addEventListener: function(type, fn, obj) {
if (!fn) {
throw new Error("function no found");
}
if (!this.listeners[type]) {
this.listeners[type] = [];
}
var lis = this.listeners[type];
if (!lis) {
lis = [];
}
lis.push({"fn": fn, "obj": obj});
},
dispatchEvent: function(event) {
var lis = this.listeners[event.type];
if (lis && lis.length) {
for (var i=0; i<lis.length; i++) {
event.target = this;
lis[i].fn.call(lis[i].obj, event)
}
}
},
removeEventListener: function(type, fn, obj) {
var lis = this.listeners[type],
ret = false;
if (!lis) {
return false;
}
for (var i=lis.length-1; i>-1; i--) {
if (!fn || (lis[i].fn == fn && lis[i].obj == obj)) {
delete lis[i];
lis.splice(i, 1);
ret = true;
}
}
if (!lis.length) {
delete this.listeners[type];
}
return ret;
},
hasEventListener: function(type) {
var lis = this.listeners[type]
return lis && lis.length > 0 ? true: false;
}
}
//事件类
var Event = function(type) {
this.type = type;
this.target = null;
}
Event.prototype = {
toString: function(){
return this.type;
},
//clone功能未完善
clone: function(){
return new Event(this.type);
}
}
完全仿照《Actionscript3殿堂之路》里事件发送和处理的例子,写了个demo:
//输出
var trace = function(msg) {
document.write("<p>" + msg + "</p>");
}
//OrderEvent事件类
var OrderEvent = function(type) {
OrderEvent.superclass.constructor.call(this, OrderEvent.ORDER_DISHES);
this.dishes = [];
}
YAHOO.extend(OrderEvent, Event);
OrderEvent.ORDER_DISHES = "order";
//服务生Waiter类
var Waiter = function() {}
Waiter.prototype.replyOrderFood = function(event) {
trace("你好," + event.target.name + "!你点的菜是:"+ event.dishes);
trace("我马上吩咐厨房去做");
}
//客人类Customer
var Customer = function(name) {
Customer.superclass.constructor.call(this);
this.name = name;
}
YAHOO.extend(Customer, EventDispatcher);
Customer.prototype.order = function(dishes) {
var orderDish = new OrderEvent();
orderDish.dishes = dishes? dishes: ["农家小炒", "麻婆豆腐", "烤鱿鱼"];
this.dispatchEvent(orderDish);
}
//生成客人和湘缘服务员
var bang = new Customer("bang"),
xiangyuanWaiter = new Waiter();
//注册侦听器,讲服务员的replyOrderFood()方法注册为侦听“order”事件的侦听器
bang.addEventListener(OrderEvent.ORDER_DISHES, xiangyuanWaiter.replyOrderFood, xiangyuanWaiter);
//俺点菜了,菜单在俺这个类里面
bang.order();
//再点次菜,点些饮料
bang.order(["鸡尾酒","咖啡"]);
//吃完饭,移除侦听器
bang.removeEventListener(OrderEvent.ORDER_DISHES, xiangyuanWaiter.replyOrderFood, xiangyuanWaiter);
http://bangswork.googlecode.com/svn/trunk/AS3Event/Event.html
我还是倾向于 给e.fn创建一个Delegate 而不传入e. 虽然代码上并没有减少 甚至更复杂了
但是我觉得xxxEventListener接口的设计更合理了.
例如 提供一个单独的函数createDelegate用了创建委托
bang.addEventListener(
OrderEvent.ORDER_DISHES,
createDelegate(xiangyuanWaiter.replyOrderFood,xiangyuanWaiter)
);