日期:2014-05-16  浏览次数:20328 次

ExtJs源码分析与学习—ExtJs事件机制(四)

ExtJs组件事件——ExtJs自定义事件的处理

????? 下面通过对组件的事件对象和事件机制两个方面分别对源码进行分析。

组件事件对象

????? ExtJs组件事件对象是通过Ext.util.Event类来完成的,其实在浏览器元素事件中部分功能的实现已用到了该类。下面看该类的实现代码

?

EXTUTIL.Event = function(obj, name){
    this.name = name;//事件名
    this.obj = obj;//作用域
    this.listeners = [];//多个监听函数的集合
};

?这里说明一点,ExtJs为了实现更高的压缩,把Ext.util定义为常量EXTUTIL来处理。
该类是个集合类,它保存了多个监听函数,同时还需要和某个事件名对应起来。该类还实现了存放、删除、查找监听函数等相关操作。下面看具体的实现方法。

?

addListener : function(fn, scope, options){
        var me = this,
            l;
        scope = scope || me.obj;
        if(!me.isListening(fn, scope)){//判断fn参数是否已经有同作用域的函数存在
            l = me.createListener(fn, scope, options);//集合并不是保存监听函数,而是存放该函数的相关信息
            // if we are currently firing this event, don't disturb the listener loop
            // 如果当前事件正在执行,为了不改变该事件的监听函数集合,重新复制当前集合,然后赋给listeners
            // 并利用me.listeners.push(l)加入新的监听函数,而原来的集合中正在被执行的代码在使用完成之后会垃圾回收,下一次执行的是新的集合,注意这种方式的实现
            if(me.firing){ 
                me.listeners = me.listeners.slice(0);//实现了复制功能
            }
            me.listeners.push(l);
        }
},

?

存放监听函数,首先判断fn参数是否已经有相同作用域的函数存在,如果存在则不会重复增加。l = me.createListener(fn, scope, options);代码返回的值并不是集合要保存的监听函数,而是存放该函数的相关信息,格式为:
this.listeners = [{fn : fn, scope : scope, options : o, fireFn: h}]
下面看createListener是怎么包裹监听函数的。

?

createListener: function(fn, scope, o){
        o = o || {};
        scope = scope || this.obj;
        var l = {
            fn: fn,
            scope: scope,
            options: o
        }, h = fn;
        if(o.target){
            h = createTargeted(h, o, scope);
        }
        if(o.delay){
            h = createDelayed(h, o, l, scope);//延迟运行
        }
        if(o.single){
            h = createSingle(h, this, fn, scope);//该监听函数只运行一次
        }
        if(o.buffer){
            h = createBuffered(h, o, l, scope);//缓存运行
        }
        l.fireFn = h;
        return l;
},

?

该函数中关于delay和buffer的区别,将在后续(定时器的讲解)给出答案。
创建完监听函数对象后,我们来看看他是如何执行的以及多个注册的监听函数执行顺序。ExtJs是通过Ext.util.fire来执行的,代码如下

?

    fire : function(){
        var me = this,
            listeners = me.listeners,
            len = listeners.length,
            i = 0,
            l;

        if(len > 0){
            me.firing = TRUE;//表明正在执行
            var args = Array.prototype.slice.call(arguments, 0);
            for (; i < len; i++) {
                l = listeners[i];
                if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
                    return (me.firing = FALSE);
                }
            }
        }
        me.firing = FALSE;
        return TRUE;
    }

?该函数通过传入的参数,来确定要执行的事件名(监听函数)

?

?

另一个比较重要的方法就是删除监听函数,代码如下

?

    /**
     * 删除监听函数(事件)
     */
    removeListener : function(fn, scope){
        var index,
            l,
            k,
            me = this,
            ret = FALSE;
        if((index = me.findListener(fn, scope)) != -1){
            if (me.firing) {
                me.listeners = me.listeners.slice(0);
            }
            l = me.listeners[index];
            if(l.task) {
                l.task.cancel();
                delete l.task;
            }
            k = l.tasks && l.tasks.length;
            if(k) {
                while(k--) {
                    l.tasks[k].cancel();
                }
                delete l.tasks;
            }
            me.listeners.splice(index, 1);//删除
            ret = TRUE;
        }
        return ret;
},

?

而方法clearListeners为删除所有监听函数

?

clearListeners : function(){
        var me = this,
            l = me.listeners,
            i = l.length;
        while(i--) {
            me.removeListener(l[i