`

Ext extend

阅读更多


第一章   必须理解Ext.extend 函 数

显然了,从函数名就可以看出来,这个函数是整个Ext 类 库的基石,之一。

笔者初看这个函数,感觉比较迷糊,只知道它完成了继承的功能,但是里面到底做了什么操作,并不太好理 解。

网络上有很多帖子和文章来解析这个函数,但笔者感觉并不是很到位。不知道是因为这些帖子的作者没有能完 全理解这个函数还是因为表达得不够清晰。

下面笔者就自己的理解来分析一下这个函数。

必须的 前置概念有三个:prototypeconstructor 、 “闭包”

没有这 三个概念的请务必先看第123 段代码,很了解的直接看第4 段代码就可以了。

1 prototype

   RectAngle=function (width,height){

      this.width=width;

      this.height=height;

   }

   RectAngle.prototype.area=function(){

      return this.width*this.height;

   }

   这段代码似曾相识吧?来自《JavaScript 权威指 南》。功能很简单的啦,定义个“矩形”的构造函数,有长和宽两个参数。

   然后在RectAngleprototype 里面增加一个计算面积的函数area.

   这样每次在var rect=new RectAngle() 的时候,就可以对rect 对象调用area() 函数了,因为rect 对象从RectAngleprototype 里面继承了area() 函数。

   这就是“JavaScript 基于原型继承”的简单理解。

2 constructor

   根据《JavaScript 权威指南》上面的解释,每个函数 都有一个prototype 属性,构造函数也是函数,所以也有prototype 属性。prototype 属性在定义 函数的时候会自动创建并初始化. 也就是说, 在 写下RectAngle=function(widht,height){//...} 的时候,RectAngleprototype 属性就已经 被创建了, 这个时候prototype 里面 只有一个属性, 它就是constructor( 构 造器), 这个constructor 指回了RectAngle 函数本身。这样就形成了一个圈一样的链条, 可 以实验一下这种调用:

   RectAngle.prototype.constructor.prototype.constructor... 这个调用是比较变态的咯,如果你能看懂,你肯定琢磨过这个问 题,呵呵。笔者也是琢磨了比较长的时间才明白其中的含义的。

当然,不明白这种变态写法也没关系的,毕竟每个哪个变态的人会在实际应用的时候写这种东西。

   言归正传,对于每个RectAngle 的实例来说, 例如var rect=new RectAngle(10,10) rect.prototype 会指向构造函数RectAngleprototype ,也就是说所有的实例都会共享同一份RectAngle.prototype

   如此,就不需要分配那么多内存给每个实例来存储prototype 属 性了。

3 、“闭 包”( 代码来自《JavaScript 权威 指南》)

   RectAngle=function(width,height){

        this.getWidth=function(){return width};

        this.getHeight=function(){return height};

   }

   RectAngle.prototype.area=function(){

      return this.getWidth()*this.getHeight();

   }

   发现了吧? 这段代码和第1 段的构造函数是不同的.RectAngle.prototype.area 这个函数也可以看出来,除了RectAngle 构造函数内部,外部函数无法直接访问RectAnglewidthheight 属性,只能通过执行getWidth()getHeight() 方法来 获得这两个属性的值。

   《指南》上面说,第一个发现这种写法的人是Douglas Crockford ,呵呵,真是个变态的家伙,这都能想出来!无语啊,人和人是有差距的。(笔者的名言)

   有了这种写法,就可以动态构建出功能强大的代码了,这种写法的用处是比较多的,例如像缓存调用变量、改变命名空间、 定义私有属性等。依次来解释一下这三个用处:

   ⑴定义私有属性:从上面的代码可以看出来,外部函数是没有办法直接引用widthheight 这两个属性的,比如var rect=new RectAngle(widht,height);rect.weidth??

   这么写就不行了。所以,通过RectAngle 构造器中this.getWidth() 方法就模拟出了一个私有的变量( 因 为JavaScript 没有private 这 个说法,所以只能叫模拟哦)

   ⑵改变命名空间:

   例如把上面的代码写成这样:

   RectAngle=function(width,height){

      getWidth:function(){

          var haha=function(){

              return width;

          }

          return haha;

      },

      getHeight:function(){

          var haha=function(){

              return height;

          }

          return haha;

      }

   }

   同样是可以运行的,看出来没有,两个get 函数里面实际上用 了同样名称的方法haha() ,但是没有关系,它们的命名空间是不同的,一个处于getWidth 的作用域,一个处于getHeight 的 作用域。当然在外部调用getWidth() 方法的时候,实际运行的是里面对应的haha() 方法。

   ⑶缓存变量:

   Java 或者C++ 的 作用域概念类似,一个方法中局部变量(方法的参数也可以看成是局部变量的一种),在方法运行完之后就会实效并释放内存。

   例如var rect=new RectAngle(width,height); 按理说,在构造函数执行完毕之后,widthheight 这两个变量就应该释放内存了,但是通过类似这种this.getWidth=function(){return width} 的定义,widthheight 变 量并不释放内存,否则在外部调用getWidht() 的时候,就无法返回对应对象的width 值了。

   (“闭包”是稍微复杂的概念,在很多的脚本语言里面都有这个特性,JAVA 中 目前是没有这个概念,据说JAVA7 将会添加“闭包”特性。但是笔者认为,作为一种重量级的语言, 并不是什么特性都要有,像“闭包”这样的东西,在重量级语言里面,稍有不慎“内存泄露”起来是so easy 的! 如果写得再变态一点,很多局部变量都可以“逃出作用域”,变成内存孤岛(没有函数可以释放它,只能看着它干瞪眼)。

4 、好 了,有了上面的简单解释,可以来分析Extextend 这 个函数了。

   首先还是把《指南》里面的继承的例子说一下,以便于理解(你很熟悉?跳过吧。)

   RectAngle=function(w,h){

      this.w=w;

      this.h=h;

   }

   RectAngle.prototype.area=function(){

      return this.w*this.h;

   }

   写个子类来继承RectAngle ,这个子类叫做有颜色的矩 形ColoredRectAngle ,多一个color 属 性

   ColoredRectAngle=function(color,w,h){

      // 首先调用父类的构造函数来拷贝wh 属性,这样的话,ColoredRectAngle 也 就有了wh 两个属性

      // 为什么不直接this.w=w;this.h=h? 倒,这样 的话,你还用继承干嘛呀?

      RectAngle.call(this,w,h);

      this.c=color;

   }

   上面已经把wh 属 性拷贝到子类中来了,父类的prototype 里面还有个area 方 法也得想办法拷贝进来,注意了,这是精彩的部分,不能错过哦。

   ColoredRectAngle.prototype=new RectAngle();// 这个写 法其实包含了很多内容哦,我们把它拆开来写会更好理解

   var rect=new RectAngle();

   ColoredRectAngle.prototype=rect;// 怎么样,含义是一样的吧?

   好,开始分析这两句话。rectRectAngle 的实例(废话,它是由RectAngle 构 造函数构造出来的,当然是它的实例了!),但是

   在构造rect 的时候,没有传参数给它,这样的话在rect 这个对象里面wh 这两个属性就是null( 显然必须的)

   既然rectRectAngle 的 实例,那么它的prototype 会指向RectAngle.prototype , 所以rect 对象会拥有area() 方法。

   另外,rect.prototype.constructor 指 向的是RectAngle 这个构造函数(显然必须的)。

   好,现在ColoredRectAngle.prototype=rect , 这一操作有三个问题,第一,rectwh 被放到ColoredRectAngle.prototype 里 面来了,第二,rect.prototype.area() 这个方法也到了ColoredRectAngle.prototype 里面了,当然,完整的访问area() 方法路径应该是ColoredRectAngle.prototype.prototype.area() , 但是因为JavaScript 的自动查找机制,放在prototype 里 面的属性会被自动找出来(加入从对象的直接属性里面找不到的话。)这样就没有必要写完整的访问路径了,直接写ColoredRectAngle.area() 就 可以找到area() 了,看上去就好像ColoredRectAngle 也 拥有了area() 方法。

   值得注意的一点是,在执行RectAngle.call(this,w,h); 这 一步的时候我们已经把wh 两个属性拷贝到ColoredRectAngle 里面了,这里我们不再需要rect 里 面这两个值为nullwh

   所以,直接把它们删除了事,免得浪费内存。

Delete ColoredRectAngle.prototype.w;

delete ColoredRectAngle.prototype.h;

   OK ,到了这一步,看起来模拟继承的操作就算大功告成了,父类RectAnglewh 属性通过RectAngle.call(this,w,h) 拷 贝进来了, 父类prototype 里面的方 法也拷贝进来了,没用的废物(rect 里面,也就是ColoredRectAngle.prototype 里 面,值为nullwh) 也剔除掉了。

   看上去世界一片和谐。但是... 还有一个暗藏的问题,请看: 第三:这个时候ColoredRectAngle 类的constructor 指 向错了。

   本来,如果没有ColoredRectAngle.prototype=rect 这 步操作,ColoredRectAngle.prototype 就是JavaScript 自动创建出来的那个prototype , 这个prototype 有个constructor , 指向了ColoredRectAngle 构造函数自己.

但是,现在ColoredRectAngle.prototype=rect , 如果现在来访问ColoredRectAngle.prototype.constructor , 那么,根据自动查找机制,会找到rect.prototype.constructor, 但这个constructor 指向的是父类RectAngle 构 造函数,这个就不符合prototype 的游戏规则了。因为,如果此时

var coloredRectAngle=new ColoredRectAngle('red',10,10) alert(coloredRectAngle.constructor);

得到的是父亲RectAngle 的构造函数,从 面向对象的观点看,这个结果是可以理解的,毕竟,子类对象也可以看成是父类对象。

   但是,这样的话对于ColoredRectAngle 的实例 来说,就不能确切地知道它的constructorColoredRectAngle 了。

   所以,需要手动地把ColoredRectAngle.prototype.constructor 设 置回来。于是有了这一步:ColoredRectAngle.prototype.constructor=ColoredRectAngle.

   OK ,看完以上内容,如果你的意识仍然清醒,那就恭喜你了。否则,再仔细看看吧。

  

 

   正式开始分析Ext.js 里面Ext 这个全局对象的extend 方法。

   完整的代码清单如下:

   extend : function(){

            // inline overrides

            var io = function(o){

                for(var m in o){

                    this[m] = o[m];

                }

            };

            var oc = Object.prototype.constructor;

 

            return function(sb, sp, overrides){

                if(typeof sp == 'object'){

                    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;

                sbp = sb.prototype = new F();

                sbp.constructor=sb;

                sb.superclass=spp;

                if(spp.constructor == oc){

                    spp.constructor=sp;

                }

                sb.override = function(o){

                    Ext.override(sb, o);

                };

                sbp.override = io;

                Ext.override(sb, overrides);

                sb.extend = function(o){Ext.extend(sb, o);};

                return sb;

            };

        }()

   首先,总体上看它是一个自执行函数,当Ext.js 这个文件 被浏览器加载的时候最外层的无参function 就被执行。这个无参的function 返回了一个有三个参数的function(sb,sp.overrides) 。 还记得上面的“闭包”吗?这种使用方式还是相当有创意的,Ext 库里面存在大量类似的闭包写法。

   var io = function(o){

        for(var m in o){

            this[m] = o[m];

        }

   };

   这一段就不用解释了,是一个用来拷贝属性的普通函数。

  

   var oc = Object.prototype.constructor; 这句定义了一个变量oc , 它的值是Object 这个根类的constructor , 大家可以把它alert 出来看,

   它是这样的

function Object(){

         [native code]

   }

   显然,JavaScript 类库并不希望我们看到这个函数里 面的实现,但是我们知道alert 出来的这个东西就是JavaScript 根 类Object 的构造函数。

  

   来分析这个带有三个参数的闭包函数,

    if(typeof sp == 'object'){

        overrides = sp;

        sp = sb;

        sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};

    }

    单是这个if 判断当时就让笔者郁闷了好久,呵呵,人和人真的 是有差距的啊!

    if(typeof sp == 'object') 这个判断是干嘛的呢?呵呵,它是用来判断你传递进来参数的个数 的。例如Ext.Panel = Ext.extend(Ext.Container, {...});

    Ext 类库里面基本都是传两个参数给extend 方法,此时,这个if 判断就要起作用啦。还不明白?硬是要说破啊。因为如果只传两个参数的话,在function(sb,sp,overrides) 看来

    第二个参数sp 不就是个“object ”麽?

    sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};

    这一句用来决定子类使用什么形式的构造函数,如果overrides 里 面有个constructor 属性,就用overridesconstructor 当作子类的构造函数。否则,创建个新的function 出 来,里面包含一句话,就是"sp.apply(this, arguments);" ,这个又是闭包的一个应用哦,在退出extend 方法之后 并没有释放局部变量sp 的内存空间。

这样的话,每次new 一 个子类的时候,第一句执行的就是sp.apply(this,arguments); 这个方法与 《指南》里面RectAngle.call(this,w,h) 完成的功能是一样的。就是把arguments 全部拷贝到子类中去。

好了,属性拷贝完成之后就要拷贝父类prototype 里面的方法了。来看看Ext 又有什么 精彩的写法:

     var F = function(){}, sbp, spp = sp.prototype;

     F.prototype = spp;

     sbp = sb.prototype = new F();

     sbp.constructor=sb;

     这几句要连起来看哦。

      按照前面《指南》里面的写法的话,应该是这样的:

     第一步:把子类的prototype 赋值为父类的实例对象。sbp=sb.prototype=new sp();

     第二步:删除不要的废属性,因为前面的if 判断里面sp.apply(this,arguments) 已经完成了属性的拷贝。

     第三步:把constructor 重新手动指回来。sbp.constructor=sb

     发现没有?如果采用《指南》里面的写法,必须要有第二步,把不要的属性都删除掉( 不删会怎样?一个是可能会存在属性覆盖的问题,另外就是内存浪费了,当new 出 很多对象来的时候,这种浪费就很可观了哦!) 。如果属性很多,岂不要写很多delete ?而且要一个一个去核对一下超类里面的属性名称,显然Ext 的 作者并不希望这么做。于是有了这几句精彩的var F=function(){} ,定义一个空函 数,里面没有属性。然后F.prototype=sp.prototype 再然后sbp=new F() 这么做的话,就把F.prototype 也 就是sp.prototype 里面的东西拷贝到sb.prototype 里 面了,同时,因为F 是个没有任何属性的函数,所以不需要再delete 任 何东西了。这句真的很精彩哦!

     这时候sb.prototype.constructorF() ,所以再来一句sbp.constructor=sb 。 这样的话就完美地完成了对父类prototype 的拷贝,而又没有把不要的属性拷进来。

     到了这里,关健的两步操作:属性拷贝、方法拷贝(prototype 里 面的) 都已经完成。

     后面的代码就比较简单了,不再解释。

     看完这篇文章你应该能理解这个核心的extend 函数到底完 成了什么操作了,如果还是不明白,我不得不承认,那还是我的错,那么请联系我吧QQ253445528 ,注明“Ext 源码分析”。说明:在5 ×8 小时的上班时间不解答问题。

     这篇文章耗费笔者近三个小时的时间,请尊重原创,转载请注明出处,谢谢。

分享到:
评论

相关推荐

    Ext_Extend 用法及解读

    ext 的详细解读,以及实际应用,与大家一起分享。

    EXT是一款强大的AJAX框架

    /* *CRUD面板基类 */ //继承EXT的Panel,创建CRUD面板 Mis.Ext.CrudPanel=Ext.extend(Ext.Panel,{……}); //限于篇幅就不列出全部代码 EXT里的继承用的是Ext.extend(组件名,{实现代码}); 要使用这个CRUD面板,...

    ext 继承(extend) 理解练习

    NULL 博文链接:https://wmch.iteye.com/blog/974219

    ExtJS的extend(Ext Designer的使用)

    NULL 博文链接:https://lggege.iteye.com/blog/602587

    EXT测试小样例--EXT测试小样例

    EXT测试小样例EXT测试小样例EXT测试小样例EXT测试小样例EXT测试小样例EXT测试小样例EXT测试小样例

    Ext grid合并单元格

    MyGridView=Ext.extend(Ext.grid.GridView,{ renderHeaders : function(){ var cm = this.cm, ts = this.templates; var ct = ts.hcell,ct2=ts.mhcell; var cb = [], sb = [], p = {},mcb=[]; for(var i = 0...

    Ext+JS高级程序设计.rar

    9.1 利用Ext.extend实现继承 254 9.2 与Ext扩展相关的预备知识 256 9.2.1 定义命名空间 256 9.2.2 重写构造函数 257 9.2.3 继承组件的一些准备 257 9.2.4 常用的辅助函数 258 9.2.5 使用xtype 258 9.3 实现一个功能...

    Ext JS in Action (第1版,深入研究Ext JS 3.0)

    Ext JS in Action is a comprehensive guide to Ext JS. By following its rich examples, patterns, and best practices, you'll ...Extend the framework and write plug-ins Watch the author develop an Ext JS app

    ext继承重写

    ext继承重写,达到了很高的水平。使用了很多方式,等等。

    ext4的MVC小例子

    extend: 'Ext.app.Controller', stores: ['GoodsStore'],//声明该控制层要用到的store models: ['GoodsModel'],//声明该控制层要用到的model views: ['goods.GoodsListView','goods.GoodsWinView'],//声明该控制...

    Ext Js权威指南(.zip.001

    4.1.3 不推荐的extend方法 / 92 4.1.4 数据及其类型检测 / 95 4.1.5 其他的基础方法 / 99 4.2 为框架顺利运行提供支持 / 107 4.2.1 平台检测工具:ext.is / 107 4.2.2 当前运行环境检测工具:ext.supports / ...

    Jquery实现$.fn.extend和$.extend函数

    前面我们扩展了bind方法和ready函数,这次我要讲一下$.fn.extend 和$.extend函数。 其他的不多说,直接切入主题吧! 先来看看这两个函数的区别:  $.fn.extend是为查询的节点对象扩展方法,是基于$的原型扩展的方法...

    EXT+STRUTS2 经典例子

    EXT 结合struts2,用json方式与ACTION通信,非常经典的入门例子! 里面对ext.extend用的很好!

    Ext.ux.UploadDialog

    Most configuration options are inherited from Ext.Window (see ExtJs docs). The added ones are: url - the url where to post uploaded files. base_params - additional post params (default to {}). ...

    ext-2.3.0+CKEditor 3.0.1+ckfinder_asp_1.4配置详解及工程源码

    Ext.extend(Ext.form.CKEditor, Ext.form.TextArea, { onRender : function(ct, position){ if(!this.el){ this.defaultAutoCreate = { tag: "textarea", autocomplete: "off" }; } Ext.form.TextArea....

    Extjs 继承Ext.data.Store不起作用原因分析及解决

    关于这个原因有很多种,我只说下我遇到的 我这样 写Store来复用的 代码如下: DocStore = Ext.extend(Ext.data.Store,{ initComponent:function(){ this.proxy = new Ext.data.HttpProxy({url:this.url}); this....

    jQuery的extend方法【三种】

    jq中的extend在面试中经常会被问道,今天我总结一个下有关于extend的用法三种进行对比,可能不全,希望大家指点, 用法一: $.extend({}) ,为jQuery类添加方法,可以理解为扩展静态方法 用法二:$.fn.extend({}) ...

    详解jQuery插件开发中的extend方法

    Jquery的扩展方法extend是我们在写插件的过程中常用的方法,该方法有一些重载原型,dest是要整合的空间可以使{} 或者不写 src是一个JSON表达式表示的javascript对象…. 因此里面可以添加方法属性等等… 我么通过不同...

    Vue.extend实现挂载到实例上的方法

    本文实例讲述了Vue.extend实现挂载到实例上的方法。分享给大家供大家参考,具体如下: 这里主要是做个笔记 根据官网的说法,Vue.extend:是使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。 ...

    python append、extend与insert的区别

    最近在自学Python语言,看到向列表增加更多数据时被append(),extend(),insert()方法绕晕了。 append 和extend都只需要一个参数,并且自动添加到数组末尾,如果需要添加多个,可用数组嵌套,但是 append是将...

Global site tag (gtag.js) - Google Analytics