Ext中的事件机制是在 Ext.util.Observable 中定义的,举一个例子来说明事件机制,先看一下代码,然后慢慢说
person.js
Ext.namespace("com.meizhi"); /* 定义NameSpace的别名 */ Mz = com.meizhi; Mz.Person = function() { /* 定义事件 */ this.addEvents( "nameChange", "sexChange" ); }; Ext.extend(Mz.Person, Ext.util.Observable, { name:"", sex:"", setName:function(_name){ if(this.name != _name) { /* 发布事件 */ this.fireEvent("nameChange", this, this.name, _name); this.name = _name; } }, setSex:function(_sex){ if(this.sex != _sex){ /* 发布事件 */ this.fireEvent("sexChange", this, this.sex, _sex); this.sex = _sex; } } });
看JS文件中的定义
Ext.util.Observable 维护了一个events 对象的数组,并提供了更加方便的对于事件的封装和调用机制。(参考:http://www.cnblogs.com/meetrice/archive/2008/05/23/1206108.html )
addEvents():绑定事件,看一下它的源代码
addEvents : function(o){ if(!this.events){ this.events = {}; } if(typeof o == 'string'){ for(var i = 0, a = arguments, v; v = a[i]; i++){ if(!this.events[a[i]]){ // 将传入的事件名称注册到事件列表中 this.events[a[i]] = true; } } }else{ Ext.applyIf(this.events, o); } }
该方法实际上就是在 Person 对象上绑定了两个没有任何实现的事件名 ,这样 Person 对象就具有了两个空的Event对象(绑定可以执行操作的Event对象使用 addlistener 方法)。
fireEvent():发布事件,也就是触发绑定的事件。源代码中的定义
fireEvent : function(){ if(this.eventsSuspended !== true){ //通过addEvents()注册的事件会被封装成events对象 var ce = this.events[arguments[0].toLowerCase()]; if(typeof ce == "object"){ //触发事件对象 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1)); } } return true; }
现在Person类的结构就很清楚了,
在person.js中完成是事件的定义和发布,那事件是在什么时候被订阅的呢? 事件触发之后又要进行哪些操作呢?
person.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Event</title> <link type="text/css" rel="stylesheet" href="../../ext/resources/css/ext-all.css"> <script type="text/javascript" src="../../ext/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="../../ext/ext-all.js"></script> <script type="text/javascript" src="person.js"></script> <script type="text/javascript"> var _person = null; button_click = function() { _person.setName(prompt("请输入姓名", "")); _person.setSex(prompt("请输入性别", "")); } Ext.onReady(function(){ var txt_name = Ext.get("txt_name"); var txt_sex = Ext.get("txt_sex"); /* 构建Person类 */ _person = new Mz.Person(); /* 订阅事件 */ _person.on("nameChange", function(_person, _old, _new){ txt_name.dom.value = _new; }); /* 订阅事件 */ _person.on("sexChange", function(_person, _old, _new){ txt_sex.dom.value = _new; }); /* 订阅事件 */ _person.on("nameChange", function(_person, _old, _new){ document.title = _new; }); }); </script> </head> <body> 姓名:<input type="text" id="txt_name" maxlength="10" /><br/> 性别:<input type="text" id="txt_sex" maxlength="10" /><br/> <input type="button" value="输入" onclick="button_click()"/> </body> </html>
HTML文件页面中定义了两个输入框,和一个按钮,通过Ext.onReady(),页面初始化后首先执行里面的代码
在点击按钮的时候,调用 button_click 方法来给 person 实例属性赋值。
在订阅事件的时候,用到的是 Observable.on 方法,实际上它是 Observable.addListener 的缩写(Ext里面的缩写好多啊...),源代码中是这样定义的
Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
绑定事件真正起作用的方法就是addListener()方法,源码中的定义
addListener : function(fn, scope, options){ scope = scope || this.obj; if(!this.isListening(fn, scope)){ var l = this.createListener(fn, scope, options); if(!this.firing){ this.listeners.push(l); }else{ // if we are currently firing this event, don't disturb the listener loop this.listeners = this.listeners.slice(0); this.listeners.push(l); } } }
最后看一下效果:
顺便说一下例子中涉及到的其他的方法:
onDocumentReady : function(fn, scope, options){ if(!docReadyEvent){ initDocReady(); } if(docReadyState || Ext.isReady){ // if it already fired options || (options = {}); fn.defer(options.delay||0, scope); }else{ docReadyEvent.addListener(fn, scope, options); } }
在这个例子中,我们订阅了两次 nameChange 事件,这也是Ext中很重要的一个机制 -- 事件列表 ,在整个应用当中,公布一个事件,这个事件可以被多次订阅,这一点是很常用的,实现了一个完善的应用体系。
通过上面的例子也可以看出Ext的一些缺点:完成这样一个 事件定义、发布、过程所用到的代码比较复杂,不够简洁,要是更加直观一些就更好了,可以让人更加容易理解一些。