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

分类:技术文章 Tags:
评论

*

*

2010年4月18日 18:29

我还是倾向于 给e.fn创建一个Delegate 而不传入e. 虽然代码上并没有减少 甚至更复杂了
但是我觉得xxxEventListener接口的设计更合理了.

例如 提供一个单独的函数createDelegate用了创建委托
bang.addEventListener(
OrderEvent.ORDER_DISHES,
createDelegate(xiangyuanWaiter.replyOrderFood,xiangyuanWaiter)
);

2010年4月18日 18:39

@fins 嗯,写的过程已经有试过了,让fn跟obj绑定,
Function.prototype.bind = function(obj) {
var method = this;
return function() {
return method.apply(obj, arguments);
};
}
bang.addEventListener(
OrderEvent.ORDER_DISHES,xiangyuanWaiter.replyOrderFood.bind(xiangyuanWaiter))
);
但显得比上面的麻烦,所以这里只给了最简单的~

2010年4月24日 16:48

请问下,你在那个著名的tw**ise程序上,我想在index.php加以下代码,不知道怎么加,你能帮我看看吗?谢谢!

if($_SERVER[‘HTTP_ACCEPT_LANGUAGE’]==’zh-cn’)
echo ‘#conash3D0 {display:none}’;