zoukankan      html  css  js  c++  java
  • Extjs学习笔记之八——继承和事件基础

    只有有了事件,页面才能真正的“活”起来。Extjs的事件也是经过良好封装的,对于事件的处理都由Ext.uitl.Observable类提供接口。这里接口的意思是Observable实际上起了一个抽象类的作用,Extjs中有大量的组件都是继承自这个类的。这个类提供了一些基本的方法比如addEvents,addlistener,fireEvent等等。

    本文暂时不介绍如何使用extjs的组件响应事件,而是介绍Extjs的事件的一些实现原理。整个Extjs框架都是以一种面向对象的方式开发的,所以理解Javascript中的继承也很重要。我前面的一篇文章 补点基础:Javascript中的类和闭包 也是为这篇做准备。另外,博客园内还有一个写的很好的系列 JavaScript继承详解. 他主要是根据Douglas Crockford的两篇文章写的。 其实实现继承的原理都差不多,大家可以参考阅读。

    Extjs实现继承的函数是一个很核心的函数Ext.extend,extend方法有两个重构版本,第一个接受两个参数,第一个是extend( Function superclass, Object overrides ) ,第二个是extend( Function subclass, Function superclass,Object overrides ) : Function,第二个版本是在subclass的基础上。superclass就是超类的构造函数,overrides是一个对象,里边的属性就是要覆盖父类的属性。继承了父类的子类具有父类的prototype中的所有方法。并且子类可以覆盖父类的方法(override),更进一步,子类的每个对象也可以覆盖父类的方法。其实我觉得这个函数没什么作用,修改prototype的效果是等效的,当然,extjs的目的肯定是要把prototype这个神奇的东西完全屏蔽起来,使程序员能够像处理其他语言一样来处理Javascript。当然,即使如此,它的继承和一般的继承还是有些不同的,下面先看个例子,准备好一个Person类

    Person = function(name) {
               this.name = name;
               this.fn = function() { alert('I am a person') };
           }
       Person.prototype.print=function(){ alert('I am a person');}
       Person.prototype.showAge = function() { alert('I am older than 0'); }
       Person.prototype.showName = function() { alert('Show Name:'+this.name) };
       var per = new Person('Tom');
       per.showName();
    子类:
    Student = function(id) {
            this.id = id;
        }
    Student.prototype.showID = function() { alert(this.id); }  //子类的方法
    继承:
    Ext.extend(Student, Person);
    stu.showName();  !!没有结果!stu没有name的定义
    stu.fn();        !!没有结果 
    stu.showID();    !!!还是没有结果
    到此我们已经发现了一些不同:在父类的构造函数中的内容是不会继承的,父类的构造函数不会被调用,子类(prototype中)已有的方法也会丢失!继续看下去,将Ext.extend下面的代码替换成:
    var stu = new Student('01');
    Student.override({ print: function() { alert('I am a student'); } }); 
    stu.override({ print: function() { alert('I am a bad student,but I won\'t affect others'); } });
    stu.print();
    stu.showAge();
    var stu2 = new Student();      
    stu2.print();
    这里的函数都能够按预期输出,showAge是执行的父类的方法,stu.print是执行的stu.override中指定的方法,而stu2执行的是Student.override中指定的方法。到这里,我们已经大概能猜出extend是如何实现的了。下面看它真正的源代码,这个方法位于Ext.js中,代码和注释如下:
    extend : function(){
        // inline overrides
        var io = function(o){               //注意这个方法的this,仅看这里并不知道这个this是什么,下面这个io会被赋值给sbp.override,也就是子类的prototype
            for(var m in o){                //从而每个子类的对象的override都会指向这个方法,如果子类对象调用了override,那么这个this就是子类的对象了。也就是
                this[m] = o[m];             //上面的例子中stu.override表现出来的效果,仅对当前对象有效。从这里可以看出,override不仅仅是传统意义上的覆盖,完全也可以
            }                               //用来添加新方法。
        };
        var oc = Object.prototype.constructor;
    
        return function(sb, sp, overrides){
            if(Ext.isObject(sp)){       //是在检测当前使用的是哪个版本的重构函数。如果sp实际上是overrides,就做些替换工作,让变量的实际意义和名称相符合。
                overrides = sp;
                sp = sb;
                sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);}; //这个没看懂……
            }
            var F = function(){},
                sbp,
                spp = sp.prototype;
    
            F.prototype = spp;              //F是父类的一个“干净”拷贝,所谓干净,是指它不会把父类中在构造函数内部定义的属性带过来。
                                            //例如 Person=function()
                                            //      {this.privateFn=new function{ some code goes here}}
                                            //那么这个privateFn对子类是不可见的,所以在构造函数中利用this定义的属性都相当于是类的私有变量。
            sbp = sb.prototype = new F();   //将子类的prototype设置为父类的prototype,继承的核心步骤。 
            sbp.constructor=sb;             //设置正确的构造函数指向,见 JavaScript继承详解
            sb.superclass=spp;              //设置父类 
            if(spp.constructor == oc){      //没看懂……,这个是干嘛用的?望高人指点
                spp.constructor=sp;       
            }
            sb.override = function(o){      //子类的重写方法,这个重写方法是函数的重写方法。它修改的是prototype。
                Ext.override(sb, o);        //见最后。
            };
            sbp.superclass = sbp.supr = (function(){  //设置原型的父类
                return spp;
            });
            sbp.override = io;                       //给子类的prototype提供override方法,这样单个实体也可以覆盖,它修改的是实体对象。注意和上面的sb的override区分。
            Ext.override(sb, overrides);             //重写
            sb.extend = function(o){return Ext.extend(sb, o);};  //给子类提供extend方法,以实现多重继承
            return sb;                               //返回子类。
        };
    }();
    下面是Ext.override的代码,比较明了的,和那个inline override相比,它就是修改的prototype:
    override : function(origclass, overrides){
               if(overrides){
                   var p = origclass.prototype;
                   Ext.apply(p, overrides);
                   if(Ext.isIE && overrides.hasOwnProperty('toString')){   // 这个是什么?IE的特殊点?
                       p.toString = overrides.toString;
                   }
               }
           }

    现在就可以开始正式介绍Extjs的事件模型了。和其他语言事件类似,首先要为一个类定义事件,其他语言(例如C#)的事件一般有一个专门的event类型,event类型实际上可以看作是委托的数组,当然委托实际上是函数,添加时间监听器(listener),就是想委托数组中添加委托(函数),所谓触发事件就是把数组中的函数统统执行一遍。Javascript也是类似的,只是Javascript的函数比那些语言强大灵活的多,因此也不需要什么event类型了。Javascript的事件看起来就像一个字符串(它内部应该也是保留了一个数组的),可以通过Observale.addEvents方法添加事件,通过Observale.fireEvent触发事件,通过Observale.addListner增加事件监听器。下面举一个没什么意义却能说明问题的例子。

    Odder = function(min, max) {
                this.min = min;
                this.max = max;
                this.addEvents('onFindOdd');
            }
            Ext.extend(Odder, Ext.util.Observable, { run:
                function() {
                    for (var i = this.min; i < this.max; i++) {
                    if (i % 2 != 0) {
                       this.fireEvent('onFindOdd',i);
                     }
                }
            }
        });
        var p = new Odder(4, 8);
        p.addListener('onFindOdd',function(n){alert(n);});
        p.run();

    Odder是这么一个类,它通过一个构造函数传入一个范围,然后寻找这个范围内的所有奇数,每找到一个就触发一个事件。我给它加一个事件处理程序,把它找到的奇数alert出来。 要注意,这里的事件处理程序的参数只能靠程序员自己保持一致,它不像委托那样强类型。

    注意,我没有采用官网上的例子:

    Employee = Ext.extend(Ext.util.Observable, {
        constructor: function(config){
            this.name = config.name;
            this.addEvents({
                "fired" : true,
                "quit" : true
            });
    
            // Copy configured listeners into *this* object so that the base class's
            // constructor will add them.
            this.listeners = config.listeners;
    
            // Call our superclass constructor to complete construction process.
            Employee.superclass.constructor.call(config)
        }
    });
    This could then be used like this:
    var newEmployee = new Employee({
        name: employeeName,
        listeners: {
            quit: function() {
                // By default, "this" will be the object that fired the event.
                alert(this.name + " has quit!");
            }
        }
    });

    我觉得官网上的例子内部还有文章,它的重载项中包含了constructor属性,给人的感觉是是重载了父类的构造函数,然后子类就会调用这个构造函数来创建,其实不是的,它改变了Javascript本身的行为,这个就和我上面标注的没有看懂的那几句代码有关系。下回再讨论。

  • 相关阅读:
    How to read a whole document content into a string onetime
    how to get the Authorization of adobe acrobat 8.0 for free
    给求职的同学的几点建议
    select() manul select() 手册
    有无一步登天之法?
    VB6.0 Excel模版,自己設定好分析餅圖,如何動態地更新分析圖的數據源呢?
    C#中的委托和事件(续)
    C# 中的委托和事件
    VB6.0 Excel的動態生成多個Sheet的方法
    VB6.0 用Excel生成數據分析餅圖例子
  • 原文地址:https://www.cnblogs.com/yinzixin/p/1642350.html
Copyright © 2011-2022 走看看