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)
);