首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Web前端 >

[Ext源码解读]事件的注册、添加与触发是怎么实现的

2012-11-23 
[Ext源码解读]事件的注册、添加与触发是如何实现的Ext提供了一套强大的事件处理机制,每个组件都有许多事件,

[Ext源码解读]事件的注册、添加与触发是如何实现的
Ext提供了一套强大的事件处理机制,每个组件都有许多事件,用户可以很方便通过动态的方式为对象添加/删除事件监听函数(在实例化的时候不是必须的),从而动态的改变或添加对象的行为,而这一切又是如何实现的呢?
阅读前请您准备好Javascript基础知识(包括:prototype属性、Functin对象的apply和call方法、函数的作用域等)。
该脚本剥离了许多分支逻辑,修改了大多数函数的实现仅保留最基本逻辑,如需详细了解Ext内部请阅读Ext源代码。Enjoy it!
该脚本脱离了Ext库的依赖,可直接复制在firbug下运行,(推荐chrome的javascript控制台,功能更强大)
预期运行结果:

/** * 观察者模式,也称作订阅发布模式,体现一种一对多关系,一般用在一个对象的改变将影响其它多个对象的情况下。 *  *  */Observable = function(name) {this.name = name;this.events = {};this.addEvents('before', 'after');this.getName = function() {if (this.fireEvent('before', this.name) !== false) {console.log('My name is ' + this.name);this.fireEvent('after', this.name);return this.name;}}}Observable.prototype = {/** * 添加事件名,就是个注册罢了,可以同时注册多个;试想一下如果添加就初始化了Event对象,如果一个组件有很多事件类型, * 那么每次组件初始化它所具备的事件时要把每个事件组件都是实例化,那该是一件多么费内存的操作啊! */addEvents : function() {var a = arguments, i = a.length;while (i--) {this.events[a[i]] = true;// 这里可不是数组,这是变量的形式访问属性}},addListener : function(eventName, fn, scope) {var ce;// 事件对象或者是truece = this.events[eventName] || true;if (ce == true) {//事件默认是没有初始化的this.events[eventName] = ce = new Event(this, eventName);}ce.addListener(fn, scope);},/** * @augments 第一个是事件名,之后是监听方法需要的参数 * @return ret 进一步执行的标实 */fireEvent : function() {var a = arguments, ret = true, ce;ename = a[0], ce = this.events[ename];if (Object.prototype.toString.call(ce) === '[object Object]') {ret = ce.fire.apply(ce, Array.prototype.slice.call(a, 1, a.length));}return ret;}};Event = function(obj, name) {this.name = name;this.obj = obj;this.listeners = [];};Event.prototype = {addListener : function(fn, scope, options) {scope = scope || this.obj;if (this.firing) { // 如果正在触发监听事件,则用slice方法创建一个与原对象一样的新对象,这样不会影响正在触发的监听方法链this.listeners = this.listeners.slice(0);}this.listeners.push({fireFn : fn,scope : scope,options : options});},fire : function() {var listeners = this.listeners, args = Array.prototype.slice.call(arguments, 0, arguments.length), len = listeners.length, i = 0, l;if (len > 0) {this.firing = true;for (; i < len; i++) {l = listeners[i];// 添加监听时设置的作用域具有最高优先级,其次是当前Event对象的目标对象,都没有就是window对象if (l&& l.fireFn.apply(l.scope || this.obj || window, args) === false) {return (this.firing = false);}}}this.firing = false;        return true;}};var obj1 = new Observable('bob');obj1.addListener('before', function() {console.log('First before listener my name is ' + this.name);});obj1.addListener('before', function() {console.log('Second before listener my name is ' + this.obj1.name);return false;}, this);obj1.addListener('after', function() {console.log('First after listener my name is ' + this.name);});obj1.addListener('after', function() {console.log('Second after listener my name is ' + this.name);});obj1.getName();
var a = arguments;Array.prototype.slice.call(a, 1, a.length);

请教大家,以上代码,a就是一个数组,为什么还要通过继承的方式调用Array的slice方法呢?这不是多此一举吗?看得我头都晕了。  请问是不是有特殊作用呢?var a = arguments;Array.prototype.slice.call(a, 1, a.length);

请教大家,以上代码,a就是一个数组,为什么还要通过继承的方式调用Array的slice方法呢?这不是多此一举吗?看得我头都晕了。  请问是不是有特殊作用呢?
错了 a不是数组 arguments是函数执行时的参数对象,它只是具有array的某些方法属性罢了。还有一点,这种方式方式不能叫做继承,call方法的目的是装换作用域并传参,这里是把Array.prototype.slice这个函数的作用域转到a这个对象上;这行代码意义在于创建一个新的数组对象,它的起点和终点是原来的1到a.length,这么做是因为写fireEvent这个方法的时候第一个参数始终都是事件名,之后才是监听函数所需要的参数

热点排行