简单分析Ext.ComponentMgr 函数
在找网上找了一下关于这个函数的大致介绍:
提供一个页面中所有组件的一个注册表( Ext.Component 的实例,或者任何子类),这样可以通过 component id 方便此访问它们 此对象也提供了可用Componentclasses的一个注册表 ,以便于记忆的字符串为key,叫做Component的 xtype. xtype . 当创建一个完整的、包含配置项的对象时,它为一个完全使用Ext的页面 提供了一种避免实例化子组件方法。子组件可以被简单指定为一个配置对象 ,只要指定正确的xtype ,这样当Component需要渲染时, 可以找到正确的类型进行懒初始化。
今天在分析Ext.form.FormPanel组件,在一地方卡了很长时间,其中就涉及到Ext.ComponentMgr。特在这里记下,免得以下又会被卡住,也希望看到此篇文章的同行有所收获
<script type="text/javascript"> Ext.onReady(function () { var panel = new Ext.form.FormPanel({ renderTo: 'formPanel', width: 400, height: 500, layout: "form", items: [{ xtype:'fieldset', columnWidth: 0.5, title: 'Fieldset 1', collapsible: true, autoHeight:true, defaults: { anchor: '-20' // leave room for error icon }, items: [ {xtype:'customType-0', fieldLabel: 'Field 0' }, { xtype:'customType-1', fieldLabel: 'Field 1' }, { xtype:'customType-2', fieldLabel: 'Field 3' } ] }] }); }); </script>
图(1)FormPanel类的继承结构图
当实例化FormPanel则会调用Ext.Component的constructor,如果你不知道为什么,可以先看下之前的文章就会明白.当调用Ext.Component的constructor时候
Ext.Component = function(config){ //... this.initComponent(); //... }
this.initComponent将会调用FormPanel的initComponent,这将会沿着FormPanel的继承链一直向上回溯至Ext.Component 的initComponent方法,在这一系列调用initComponent方法的过程之中我们将目光锁定到Ext.Container的initComponent方法
Ext.Container = Ext.extend(Ext.BoxComponent, { //... initComponent:function(){ //... var items = this.items; if(items){ delete this.items; this.add(items); } }, add : function(comp){ this.initItems(); var args = arguments.length > 1; if(args || Ext.isArray(comp)){ var result = []; Ext.each(args ? arguments : comp, function(c){ result.push(this.add(c)); }, this); return result; } var c = this.lookupComponent(this.applyDefaults(comp)); //... }, applyDefaults : function(c){ //... }, lookupComponent : function(comp){ if(Ext.isString(comp)){ return Ext.ComponentMgr.get(comp); } else if(!comp.events){ return this.createComponent(comp); } return comp; }, createComponent : function(config, defaultType){ if (config.render) { return config; } var c = Ext.create(/*省略*/); //... } //... });
当调用Ext.Container的initComponent方法时候,将会牵扯到一系列的方法的调用,代码如上
当最开始调用this.add(items)时,此时的this的可执行上下文环境是panel( var panel=new Ext.form.FormPanel(/* ... */); ),此时items的xtype为fieldset
图(2)
接下来进入到Ext.Container的add函数中Ext.isArray(comp)显示是成立的,从图(2)就能看得出来。接下是Ext.each方法调用result.push(this.add(c))将会递归调用add
方法
图(3)
接上面,当递归调用add时传的参数截图如图(3) if(args||Ext.isArray(comp)){/* ...*/} 很显然不会执行,这样程序就执行到 this.lookupComponent(this.applyDefaults(comp)), 执行this.lookupComponent(/* ... */)方法,Ext.isString(comp)返回false 因为comp是object,因此将调用this.createComponent(comp),进入到方法。我们把目光锁定到Ext.create方法上,由于Ext.create = Ext.ComponentMgr.create 下面是这个函数的源码
Ext.ComponentMgr = function(){ //... return { create : function(config, defaultType){ return config.render ? config : new types[config.xtype || defaultType](config); } //... }; }()
看上面的代码出现了new,当返回的时候this的可执行上下文环境就发性了变化,将不再是之前的panel了,而是新创建的Fieldset。当递归调用结束返回到Ext.Component中的this.add(items)时候,由于this的可执行上下文环境发现了变化,此时items截图如下
图(4)
从又会重复之前的动作。
图(5)
IE提示出现以上错误,crtl+G快速定位到10462行
图(6)
用VS调试
图(7)
以上是new types[/* ...*/](config)中的types的一部分截图,而此时config.xtype为"customType-0" 在types中没有,理所当然要报错,因为自己打算写相应的扩展,故用了xtype分别为"customType-0","custom-1","custom-2"。看到types数组中有许多项,我在Ext.ComponentMgr函数翻了半天,就是不明白这么多项是怎么来的,百思不其解???
Ext.ComponentMgr = function(){ var all = new Ext.util.MixedCollection(); var types = {}; var ptypes = {}; return { //... all : all, types : types, ptypes: ptypes //... } }();
看到Ext.ComponentMgr函数后面的括号,想到该函数在加载之后会立刻执行。以下是该函数执行完之后的截图
图(8)
执完该函数完之后,types中一项也没有,感觉太奇怪了,继续单步调试下去到了 Ext.reg('component', Ext.Component);
因为 Ext.reg = Ext.ComponentMgr.registerType;
Ext.ComponentMgr = function(){ //... return { //... registerType : function(xtype, cls){ types[xtype] = cls; cls.xtype = xtype; } //... }; }();
看到这个registerType函数之后,一下子就明白了 当执行完 Ext.reg('component', Ext.Component);
图(9)