ExtJS4.0的类系统是整个框架的基础且核心的架构设施,其它所有的功能扩展都是建立在类系统上的。在ExtJS4.0中,类系统相对以前的版本有大幅度的改变,在以前的版本中,定义一个新类是在一个已经存在的类如Object的基础上调用
在ExtJS4.0中,与类创建相关的js文件主要有src/core/src/class中的Base.js,Class.js,ClassManager.js,Loader.js。其中Base.js定义了Base类,Base类是ExtJS4.0中所有通过Ext.define定义的类的直接或间接父类,Base类中定义了一些通用的实例与静态属性与方法;Class.js文件中定义了Class类,所有通过Ext.define定义的类都是Class的实例(其实是Class构造函数中的newClass类);ClassManager.js文件中定义了ClassManager单例,它主要负责对类的管理,如类的创建与类实例化等;Loader文件中定义了Loader单例,实现了ExtJS4.0中动态加载类的功能。
以ExtJS中的Component类为例,它的类创建代码如下:
一个类创建前与创建后分别会有前置处理器与后置处理器对类进行装饰以增强类的功能,下面一张图片对类的创建过程作了一个详细的说明:
类创建前会有loader,extend,mixins,config,statics对类增加配置信息,且这些preprocessor是按顺序进行的,postprscessor主要包括alias(为类设置别名)与legacy等。
Ext.define实际上是ClassManager中的create方法的别名,ClassManager是一个单例,里面主要定义了与类的创建(create)与实例化类(instantiate)相关的方法。所以
下面是Class的核心代码:
ExtJS4.0会首先加载Base,Class,ClassManager,Loader及其所依赖的类及其它一些工具类或方法,也就是ext-debug.js或ext.js中的代码,其中注册前置处理器的代码是在Class中定义的,代码如下:
在Class中注册的preprocessor有['extend', 'statics', 'inheritableStatics', 'mixins', 'config'],同时默认的前置处理器也是这几个。在Class中也定义了一个叫做setDefaultPreprocessorPosition的方法,其作用是将preprocessor插入到默认preprocessors的指定位置,而这个要插入的preprocessor必须是已经注册的(即通过registerPreprocessor方法注册到Class中的preprocessors),其定义如下:
其中offset取值可以是‘first’,‘last’(表示插入到队头还是队尾),‘before’,‘after’(相对relativeName插入在其前或其后)。
在ClassManager中给要创建的类注册了className前置处理器,主要作用是从data中获取$className给要创建的类添加$className属性,方便以后的调试及获取类的信息:
并通过
在Loader中给要创建的类注册了loader前置处理器,其主要作用是确保data中的extend,requires及mixins配置所指定的类已经加载,如果这些类还没加载则通过异步或同步的方式加载这些类,loader前置处理器会根据dependencyProperties(其值为['extend', 'mixins', 'requires'])中指定的preprocessor的顺序将data中对应的preprocessor要求使用的类名转换成实际的类,如会将
经过上述一番注册,此时的前置处理器及顺序为['className','loader','extend','statics','inheritableStatics','mixins','config'],
然后就会按照顺序调用这些preprocessor。className与loader前置处理器及其作用在前面已经分析过,经loader处理后,data中extend,requires及mixins指定的类被加载且被转换成了实际的类,之后会调用extend前置处理器,代码如下:
其实裁剪一下,此继承的思想大致如下:
statics前置处理器为类增加static属性或方法,代码如下:
实现比较简单,直接将data中的statics中指定的属性或方法掺进newClass中即可。
inheritableStatics前置处理器设置可被继承的static属性,代码如下:
mixins前置处理器将指定类的prototype中的属性掺进当前类的prototype中,代码如下:
Base类中定义了mixin静态方法,此静态方法被新创建的newClass继承,所以可以直接在newClass上调用mixin将data.mixins中的类掺进来,其代码如下:
其中flexSetter是Ext.Function.flexSetter中的一个工具函数,在整个ExtJS4.0框架中用到的地方很多,它接收一个需要两个参数的函数作参数,作用是返回一个函数,该函数接收两个参数,内部通过对返回函数参数调用flexSetter的形参函数将返回函数中的参数信息添加到调用对象中,说起来很拗口,代码如下:
最后的一个前置处理器是config,将data中的config对象中的信息添加newClass的prototype中,此前置处理器会自动为config中的每一个没有配置setter,getter,apply方法的属性添加setter,getter与apply方法,并将这些方法临时存放到data中,其中apply方法在对象调用setter方法时会被调用,代码如下:
所有的上述前置处理器被调用后,就会调用刚才注册的onBeforeClassCreated方法,代码如下:
其中cls是被创建的newClass,其implement静态方法也是从Base继承而来,其作用是将参数对象(data)中的属性添加到调用对象(newClass)的prototype中,刚才在config前置处理器中生成并存放在data中的setter,getter及apply方法也会在此被添加到newClass的prototype中,代码如下:
至此整个newClass的创建已经完成,后面就是调用onClassCreated回调函数通过后置处理器对已经创建的类进行处理以方便对类的管理。
后置处理器的注册是在ClassManager中定义的,代码如下:
同时在ClassManager也定义了setDefaultPostprocessorPosition,其作用与在Class中定义的setDefaultPreprocessorPosition的作用相同,为了将指定后置处理器放到指定的位置。在ClassManger中注册的后置处理器及其顺序为:['alias', 'singleton', 'alternateClassName'],并将它们设置为默认的后置处理器,在Loader中通过setDefaultPostprocessorPosition将在其中注册的uses后置处理器放到先前注册的默认后置处理器的后面,这些后置处理器都不是必须的。其中alias给新创建类设置别名,别名的前缀若为'widget.',则为创建的类(newClass)增加xtype静态属性,这样以后创建类的实例时就可以直接使用xtype来代替实际的类名了;singleton使创建的类成为单例类,经过此后置处理器的处理后,会new一个该类的实例(即new newClass())并注册到ClassManager中,以后每次创建一个该类的实例时就会直接返回这个已经被注册到ClassManager中的实例(单例);alternateClassName可以为类设置可替代的类名,这个后置处理器主要是为了兼容4.0之前的版本库以前而定义的,由于ExtJS4.0对命名空间及类名作了较大调整,所以可以通过设置alternateClassName的方式让以前版本的类名指向的是4.0版本中新类名所指向的实际类;而uses后置处理器列出了要和Ext.define的类一同加载的类,这些类在被实例化前可能并未加载。
alias后置处理器的定义如下:
singleton后置处理器的定义如下:
此处甚为巧妙,直接new一个cls的实例,之后后面的后置处理器(fn是调用后置处理器的函数,此处是递归调用)都会在这个对象上进一步处理,最后将这个实例注册到ClassManager中,以后要创建这个类的实例时直接返回的是这个实例,也就达到了单例的效果。
alternateClassName后置处理器的定义如下:
uses后置处理器的定义如下:
所有上述的前置与后置处理器被调用结束后,就会将新创建的类注册到ClassManager中,代码如下:
至此,整个类的创建便完成了。
var myClass = Ext.extend(Object, { ... });进行扩展而来,这样我们不能方便的给新建的类添加statics属性或方法及mixins等功能,且新建的类需要依赖Object等这些要继承的类,Object也会递归的依赖它的父类,如果这些类未创建就会出现错误,所以在以前的版本中干脆把整个ext-all.js在一开始就全部加载进来并创建整个框架中定义的所有类,这样即使你在一个应用中只使用ExtJS的一个类也要把整个ExtJS的类加载进来,大大将低了程序运行效率。在extjs4.0中通过使用
Ext.define(className, members, onClassCreated);来创建类解决了上面的问题。其中className是创建的类的名字,是String类型的;members是给要创建的类添加的配置信息,主要包括requires(要求使用的类),extend(要继承的类),mixins(向创建的类中要掺进其它类的信息),config(给创建的类配置属性方法等),statics(给类增加静态属性或方法),以及其它的一些属性方法等信息,这些配置信息在后面会详细说明;onClassCreated是类创建后的回调函数。
在ExtJS4.0中,与类创建相关的js文件主要有src/core/src/class中的Base.js,Class.js,ClassManager.js,Loader.js。其中Base.js定义了Base类,Base类是ExtJS4.0中所有通过Ext.define定义的类的直接或间接父类,Base类中定义了一些通用的实例与静态属性与方法;Class.js文件中定义了Class类,所有通过Ext.define定义的类都是Class的实例(其实是Class构造函数中的newClass类);ClassManager.js文件中定义了ClassManager单例,它主要负责对类的管理,如类的创建与类实例化等;Loader文件中定义了Loader单例,实现了ExtJS4.0中动态加载类的功能。
以ExtJS中的Component类为例,它的类创建代码如下:
Ext.define('Ext.Component', { /* Begin Definitions */ alias: ['widget.component', 'widget.box'], extend: 'Ext.AbstractComponent', requires: [ 'Ext.util.DelayedTask' ], uses: [ 'Ext.Layer', 'Ext.resizer.Resizer', 'Ext.util.ComponentDragger' ], mixins: { floating: 'Ext.util.Floating' }, statics: { // Collapse/expand directions DIRECTION_TOP: 'top', DIRECTION_RIGHT: 'right', DIRECTION_BOTTOM: 'bottom', DIRECTION_LEFT: 'left', VERTICAL_DIRECTION: /^(?:top|bottom)$/ }, resizeHandles: 'all' } );
一个类创建前与创建后分别会有前置处理器与后置处理器对类进行装饰以增强类的功能,下面一张图片对类的创建过程作了一个详细的说明:
类创建前会有loader,extend,mixins,config,statics对类增加配置信息,且这些preprocessor是按顺序进行的,postprscessor主要包括alias(为类设置别名)与legacy等。
Ext.define实际上是ClassManager中的create方法的别名,ClassManager是一个单例,里面主要定义了与类的创建(create)与实例化类(instantiate)相关的方法。所以
Ext.define(className, data, onClassCreated);实际上是调用了
ClassManager.create(className, data, onClassCreated);。在此方法内部,会先将className添加到data,之后会new一个Class并返回,所以可以说Ext.define出的类都是Class这个类的实例(其实new Class后返回的是其内部的newClass类),下面是裁剪后的代码:
create: function(className, data, createdFn) { var manager = this; data.$className = className; return new Class(data, function() { var postprocessorStack = data.postprocessors || manager.defaultPostprocessors, registeredPostprocessors = manager.postprocessors, index = 0, postprocessors = [], postprocessor, postprocessors, process, i, ln; delete data.postprocessors; for (i = 0, ln = postprocessorStack.length; i < ln; i++) { postprocessor = postprocessorStack[i]; if (typeof postprocessor === 'string') { postprocessor = registeredPostprocessors[postprocessor]; if (!postprocessor.always) { if (data[postprocessor.name] !== undefined) { postprocessors.push(postprocessor.fn); } } else { postprocessors.push(postprocessor.fn); } } else { postprocessors.push(postprocessor); } } process = function(clsName, cls, clsData) { postprocessor = postprocessors[index++]; if (!postprocessor) { manager.set(className, cls); Ext.Loader.historyPush(className); if (createdFn) { createdFn.call(cls, cls); } return; } if (postprocessor.call(this, clsName, cls, clsData, process) !== false) { process.apply(this, arguments); } }; process.call(manager, className, this, data); }); },
下面是Class的核心代码:
Ext.Class = Class = function(newClass, classData, onClassCreated) { if (typeof newClass !== 'function') { onClassCreated = classData; classData = newClass; newClass = function() { return this.constructor.apply(this, arguments); };//这才是最终创建的类 } if (!classData) { classData = {}; } //创建类时可以自定义preprocessors,否则使用默认的preprocessors。 //默认preprocessors有 //['extend', 'statics', 'inheritableStatics', 'mixins', 'config'] var preprocessorStack = classData.preprocessors || Class.getDefaultPreprocessors(), //获取已经注册的preprocessors registeredPreprocessors = Class.getPreprocessors(), index = 0, preprocessors = [], preprocessor, preprocessors, staticPropertyName, process, i, j, ln; //继承Base类的静态方法,在ExtJS4.0中,通过Ext.define创建的类都是Base的直接 //或间接子类,后面在分析extend前置处理器时会详细说明,此处只把Base中的static //属性掺进newClass中。 for (i = 0, ln = baseStaticProperties.length; i < ln; i++) { staticPropertyName = baseStaticProperties[i]; newClass[staticPropertyName] = Base[staticPropertyName]; } delete classData.preprocessors; //获取总是要注册的preprocessor(如extend)及创建的类注册的preprocessor for (j = 0, ln = preprocessorStack.length; j < ln; j++) { preprocessor = preprocessorStack[j]; if (typeof preprocessor === 'string') { preprocessor = registeredPreprocessors[preprocessor]; if (!preprocessor.always) { //创建类指定了要注册的preprocessor if (classData.hasOwnProperty(preprocessor.name)) { preprocessors.push(preprocessor.fn); } } else { //总是要注册的preprocessor preprocessors.push(preprocessor.fn); } } else { preprocessors.push(preprocessor); } } //类创建后的回调函数 classData.onClassCreated = onClassCreated; //preprocessor对类作增强后,类创建前的回调函数, //即将data中指定的配置信息掺进newClass的prototype中 classData.onBeforeClassCreated = function(cls, data) { onClassCreated = data.onClassCreated; delete data.onBeforeClassCreated; delete data.onClassCreated; //将data中指定的配置信息掺进newClass的prototype中 cls.implement(data); //调用类创建后的回调函数,主要是通过postpreprocessor对 newClass增强 if (onClassCreated) { onClassCreated.call(cls, cls); } }; //preprocessor process = function(cls, data) { preprocessor = preprocessors[index++]; //所有preprocessor调用结束后调用onBeforeClassCreated if (!preprocessor) { data.onBeforeClassCreated.apply(this, arguments); return; } //调用preprocessor直到所有preprocessor处理结束 if (preprocessor.call(this, cls, data, process) !== false) { process.apply(this, arguments); } }; process.call(Class, newClass, classData); return newClass; };
ExtJS4.0会首先加载Base,Class,ClassManager,Loader及其所依赖的类及其它一些工具类或方法,也就是ext-debug.js或ext.js中的代码,其中注册前置处理器的代码是在Class中定义的,代码如下:
registerPreprocessor: function(name, fn, always) { this.preprocessors[name] = { name: name,//前置处理器的名字 always: always || false,//是否总是要注册 fn: fn //当前前置处理器的回调函数 }; return this; }
在Class中注册的preprocessor有['extend', 'statics', 'inheritableStatics', 'mixins', 'config'],同时默认的前置处理器也是这几个。在Class中也定义了一个叫做setDefaultPreprocessorPosition的方法,其作用是将preprocessor插入到默认preprocessors的指定位置,而这个要插入的preprocessor必须是已经注册的(即通过registerPreprocessor方法注册到Class中的preprocessors),其定义如下:
setDefaultPreprocessorPosition: function(name, offset, relativeName) { var defaultPreprocessors = this.defaultPreprocessors, index; if (typeof offset === 'string') { if (offset === 'first') { defaultPreprocessors.unshift(name); return this; } else if (offset === 'last') { defaultPreprocessors.push(name); return this; } offset = (offset === 'after') ? 1 : -1; } index = Ext.Array.indexOf(defaultPreprocessors, relativeName); if (index !== -1) { Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name); } return this; }
其中offset取值可以是‘first’,‘last’(表示插入到队头还是队尾),‘before’,‘after’(相对relativeName插入在其前或其后)。
在ClassManager中给要创建的类注册了className前置处理器,主要作用是从data中获取$className给要创建的类添加$className属性,方便以后的调试及获取类的信息:
Class.registerPreprocessor('className', function(cls, data) { if (data.$className) { cls.$className = data.$className; //<debug> cls.displayName = cls.$className; //</debug> } }, true);
并通过
Class.setDefaultPreprocessorPosition('className', 'first');将其变为第一个preprocessor。
在Loader中给要创建的类注册了loader前置处理器,其主要作用是确保data中的extend,requires及mixins配置所指定的类已经加载,如果这些类还没加载则通过异步或同步的方式加载这些类,loader前置处理器会根据dependencyProperties(其值为['extend', 'mixins', 'requires'])中指定的preprocessor的顺序将data中对应的preprocessor要求使用的类名转换成实际的类,如会将
{ extend: 'Ext.MyClass', requires: ['Ext.some.OtherClass'], mixins: { observable: 'Ext.util.Observable'; } }转换成
{ extend: Ext.MyClass, requires: [Ext.some.OtherClass], mixins: { observable: Ext.util.Observable; } }loader前置处理器的代码如下:
Class.registerPreprocessor('loader', function(cls, data, continueFn) { var me = this, dependencies = [], className = Manager.getName(cls), i, j, ln, subLn, value, propertyName, propertyValue; /* Basically loop through the dependencyProperties, look for string class names and push them into a stack, regardless of whether the property's value is a string, array or object. For example: { extend: 'Ext.MyClass', requires: ['Ext.some.OtherClass'], mixins: { observable: 'Ext.util.Observable'; } } which will later be transformed into: { extend: Ext.MyClass, requires: [Ext.some.OtherClass], mixins: { observable: Ext.util.Observable; } } */ for (i = 0, ln = dependencyProperties.length; i < ln; i++) { propertyName = dependencyProperties[i]; if (data.hasOwnProperty(propertyName)) { propertyValue = data[propertyName]; if (typeof propertyValue === 'string') { dependencies.push(propertyValue); } else if (propertyValue instanceof Array) { for (j = 0, subLn = propertyValue.length; j < subLn; j++) { value = propertyValue[j]; if (typeof value === 'string') { dependencies.push(value); } } } else { for (j in propertyValue) { if (propertyValue.hasOwnProperty(j)) { value = propertyValue[j]; if (typeof value === 'string') { dependencies.push(value); } } } } } } if (dependencies.length === 0) { // Loader.historyPush(className); return; } //<debug error> var deadlockPath = [], requiresMap = Loader.requiresMap, detectDeadlock; /* Automatically detect deadlocks before-hand, will throw an error with detailed path for ease of debugging. Examples of deadlock cases: - A extends B, then B extends A - A requires B, B requires C, then C requires A The detectDeadlock function will recursively transverse till the leaf, hence it can detect deadlocks no matter how deep the path is. */ if (className) { requiresMap[className] = dependencies; detectDeadlock = function(cls) { deadlockPath.push(cls); if (requiresMap[cls]) { if (Ext.Array.contains(requiresMap[cls], className)) { Ext.Error.raise({ sourceClass: "Ext.Loader", msg: "Deadlock detected while loading dependencies! '" + className + "' and '" + deadlockPath[1] + "' " + "mutually require each other. Path: " + deadlockPath.join(' -> ') + " -> " + deadlockPath[0] }); } for (i = 0, ln = requiresMap[cls].length; i < ln; i++) { detectDeadlock(requiresMap[cls][i]); } } }; detectDeadlock(className); } //</debug> Loader.require(dependencies, function() { for (i = 0, ln = dependencyProperties.length; i < ln; i++) { propertyName = dependencyProperties[i]; if (data.hasOwnProperty(propertyName)) { propertyValue = data[propertyName]; if (typeof propertyValue === 'string') { data[propertyName] = Manager.get(propertyValue); } else if (propertyValue instanceof Array) { for (j = 0, subLn = propertyValue.length; j < subLn; j++) { value = propertyValue[j]; if (typeof value === 'string') { data[propertyName][j] = Manager.get(value); } } } else { for (var k in propertyValue) { if (propertyValue.hasOwnProperty(k)) { value = propertyValue[k]; if (typeof value === 'string') { data[propertyName][k] = Manager.get(value); } } } } } } continueFn.call(me, cls, data); }); return false; }, true);
经过上述一番注册,此时的前置处理器及顺序为['className','loader','extend','statics','inheritableStatics','mixins','config'],
然后就会按照顺序调用这些preprocessor。className与loader前置处理器及其作用在前面已经分析过,经loader处理后,data中extend,requires及mixins指定的类被加载且被转换成了实际的类,之后会调用extend前置处理器,代码如下:
Class.registerPreprocessor('extend', function(cls, data) { var extend = data.extend, //Base类是所有通过Ext.define创建的类的直接或间接父类 base = Ext.Base, basePrototype = base.prototype, prototype = function() {}, parent, i, k, ln, staticName, parentStatics, parentPrototype, clsPrototype; if (extend && extend !== Object) { parent = extend; } //如果没有在data中指定要继承的类,则默认继承Base类 else { parent = base; } parentPrototype = parent.prototype; prototype.prototype = parentPrototype; clsPrototype = cls.prototype = new prototype(); if (!('$class' in parent)) { for (i in basePrototype) { if (!parentPrototype[i]) { parentPrototype[i] = basePrototype[i]; } } } clsPrototype.self = cls; cls.superclass = clsPrototype.superclass = parentPrototype; //删除data中的extend属性 delete data.extend; // 继承父类中可被继承的属性或方法 parentStatics = parentPrototype.$inheritableStatics; if (parentStatics) { for (k = 0, ln = parentStatics.length; k < ln; k++) { staticName = parentStatics[k]; if (!cls.hasOwnProperty(staticName)) { cls[staticName] = parent[staticName]; } } } // 继承父类中的config信息 if (parentPrototype.config) { clsPrototype.config = Ext.Object.merge({}, parentPrototype.config); } else { clsPrototype.config = {}; } //父类被继承后的回调函数 if (clsPrototype.$onExtended) { clsPrototype.$onExtended.call(cls, cls, data); } //为当前类注册被继承后的回调函数 if (data.onClassExtended) { clsPrototype.$onExtended = data.onClassExtended; delete data.onClassExtended; } }, true);
其实裁剪一下,此继承的思想大致如下:
function extend(SubClass,SupClass) { function F(){} F.prototype = SupClass.prototype; SubClass.prototype = new F(); SubClass.superclass = SubClass.prototype.superclass = SupClass.prototype; return SubClass; }
statics前置处理器为类增加static属性或方法,代码如下:
Class.registerPreprocessor('statics', function(cls, data) { var statics = data.statics, name; for (name in statics) { if (statics.hasOwnProperty(name)) { cls[name] = statics[name]; } } delete data.statics; });
实现比较简单,直接将data中的statics中指定的属性或方法掺进newClass中即可。
inheritableStatics前置处理器设置可被继承的static属性,代码如下:
Class.registerPreprocessor('inheritableStatics', function(cls, data) { var statics = data.inheritableStatics, inheritableStatics, prototype = cls.prototype, name; inheritableStatics = prototype.$inheritableStatics; if (!inheritableStatics) { inheritableStatics = prototype.$inheritableStatics = []; } for (name in statics) { if (statics.hasOwnProperty(name)) { cls[name] = statics[name]; inheritableStatics.push(name); } } delete data.inheritableStatics; });
mixins前置处理器将指定类的prototype中的属性掺进当前类的prototype中,代码如下:
Class.registerPreprocessor('mixins', function(cls, data) { cls.mixin(data.mixins); delete data.mixins; });
Base类中定义了mixin静态方法,此静态方法被新创建的newClass继承,所以可以直接在newClass上调用mixin将data.mixins中的类掺进来,其代码如下:
mixin: flexSetter(function(name, cls) { var mixin = cls.prototype, my = this.prototype, i, fn; for (i in mixin) { if (mixin.hasOwnProperty(i)) { if (my[i] === undefined) { if (typeof mixin[i] === 'function') { fn = mixin[i]; if (fn.$owner === undefined) { this.ownMethod(i, fn); } else { my[i] = fn; } } else { my[i] = mixin[i]; } } else if (i === 'config' && my.config && mixin.config) { Ext.Object.merge(my.config, mixin.config); } } } if (my.mixins === undefined) { my.mixins = {}; } my.mixins[name] = mixin; })
其中flexSetter是Ext.Function.flexSetter中的一个工具函数,在整个ExtJS4.0框架中用到的地方很多,它接收一个需要两个参数的函数作参数,作用是返回一个函数,该函数接收两个参数,内部通过对返回函数参数调用flexSetter的形参函数将返回函数中的参数信息添加到调用对象中,说起来很拗口,代码如下:
flexSetter: function(fn) { return function(a, b) { var k, i; if (a === null) { return this; } if (typeof a !== 'string') { for (k in a) { if (a.hasOwnProperty(k)) { fn.call(this, k, a[k]); } } if (Ext.enumerables) { for (i = Ext.enumerables.length; i--;) { k = Ext.enumerables[i]; if (a.hasOwnProperty(k)) { fn.call(this, k, a[k]); } } } } else { fn.call(this, a, b); } return this; }; }
最后的一个前置处理器是config,将data中的config对象中的信息添加newClass的prototype中,此前置处理器会自动为config中的每一个没有配置setter,getter,apply方法的属性添加setter,getter与apply方法,并将这些方法临时存放到data中,其中apply方法在对象调用setter方法时会被调用,代码如下:
Class.registerPreprocessor('config', function(cls, data) { var prototype = cls.prototype; Ext.Object.each(data.config, function(name) { var cName = name.charAt(0).toUpperCase() + name.substr(1), pName = name, apply = 'apply' + cName, setter = 'set' + cName, getter = 'get' + cName; //添加setter方法 if (!(apply in prototype) && !data.hasOwnProperty(apply)) { data[apply] = function(val) { return val; }; } if (!(setter in prototype) && !data.hasOwnProperty(setter)) { data[setter] = function(val) { //调用相应的apply方法 var ret = this[apply].call(this, val, this[pName]); if (ret !== undefined) { this[pName] = ret; } return this; }; } //添加getter方法 if (!(getter in prototype) && !data.hasOwnProperty(getter)) { data[getter] = function() { return this[pName]; }; } }); //将data中的config合并到prototype的config中,并删除之 Ext.Object.merge(prototype.config, data.config); delete data.config; });
所有的上述前置处理器被调用后,就会调用刚才注册的onBeforeClassCreated方法,代码如下:
classData.onBeforeClassCreated = function(cls, data) { onClassCreated = data.onClassCreated; delete data.onBeforeClassCreated; delete data.onClassCreated; cls.implement(data); if (onClassCreated) { onClassCreated.call(cls, cls); } };
其中cls是被创建的newClass,其implement静态方法也是从Base继承而来,其作用是将参数对象(data)中的属性添加到调用对象(newClass)的prototype中,刚才在config前置处理器中生成并存放在data中的setter,getter及apply方法也会在此被添加到newClass的prototype中,代码如下:
implement: function(members) { var prototype = this.prototype, name, i, member, previous; //<debug> var className = Ext.getClassName(this); //</debug> for (name in members) { if (members.hasOwnProperty(name)) { member = members[name]; if (typeof member === 'function') { member.$owner = this; member.$name = name; //<debug> if (className) { member.displayName = className + '#' + name; } //</debug> } prototype[name] = member; } } if (Ext.enumerables) { var enumerables = Ext.enumerables; for (i = enumerables.length; i--;) { name = enumerables[i]; if (members.hasOwnProperty(name)) { member = members[name]; member.$owner = this; member.$name = name; prototype[name] = member; } } } }
至此整个newClass的创建已经完成,后面就是调用onClassCreated回调函数通过后置处理器对已经创建的类进行处理以方便对类的管理。
后置处理器的注册是在ClassManager中定义的,代码如下:
registerPostprocessor: function(name, fn, always) { this.postprocessors[name] = { name: name, always: always || false, fn: fn }; return this; }
同时在ClassManager也定义了setDefaultPostprocessorPosition,其作用与在Class中定义的setDefaultPreprocessorPosition的作用相同,为了将指定后置处理器放到指定的位置。在ClassManger中注册的后置处理器及其顺序为:['alias', 'singleton', 'alternateClassName'],并将它们设置为默认的后置处理器,在Loader中通过setDefaultPostprocessorPosition将在其中注册的uses后置处理器放到先前注册的默认后置处理器的后面,这些后置处理器都不是必须的。其中alias给新创建类设置别名,别名的前缀若为'widget.',则为创建的类(newClass)增加xtype静态属性,这样以后创建类的实例时就可以直接使用xtype来代替实际的类名了;singleton使创建的类成为单例类,经过此后置处理器的处理后,会new一个该类的实例(即new newClass())并注册到ClassManager中,以后每次创建一个该类的实例时就会直接返回这个已经被注册到ClassManager中的实例(单例);alternateClassName可以为类设置可替代的类名,这个后置处理器主要是为了兼容4.0之前的版本库以前而定义的,由于ExtJS4.0对命名空间及类名作了较大调整,所以可以通过设置alternateClassName的方式让以前版本的类名指向的是4.0版本中新类名所指向的实际类;而uses后置处理器列出了要和Ext.define的类一同加载的类,这些类在被实例化前可能并未加载。
alias后置处理器的定义如下:
Manager.registerPostprocessor('alias', function(name, cls, data) { var aliases = data.alias, widgetPrefix = 'widget.', i, ln, alias; if (!(aliases instanceof Array)) { aliases = [aliases]; } for (i = 0, ln = aliases.length; i < ln; i++) { alias = aliases[i]; //<debug error> if (typeof alias !== 'string') { Ext.Error.raise({ sourceClass: "Ext", sourceMethod: "define", msg: "Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string" }); } //</debug> this.setAlias(cls, alias); } // This is ugly, will change to make use of parseNamespace for alias later on for (i = 0, ln = aliases.length; i < ln; i++) { alias = aliases[i]; if (alias.substring(0, widgetPrefix.length) === widgetPrefix) { // Only the first alias with 'widget.' prefix will be used for xtype cls.xtype = cls.$xtype = alias.substring(widgetPrefix.length); break; } } })
singleton后置处理器的定义如下:
Manager.registerPostprocessor('singleton', function(name, cls, data, fn) { fn.call(this, name, new cls(), data); return false; });
此处甚为巧妙,直接new一个cls的实例,之后后面的后置处理器(fn是调用后置处理器的函数,此处是递归调用)都会在这个对象上进一步处理,最后将这个实例注册到ClassManager中,以后要创建这个类的实例时直接返回的是这个实例,也就达到了单例的效果。
alternateClassName后置处理器的定义如下:
Manager.registerPostprocessor('alternateClassName', function(name, cls, data) { var alternates = data.alternateClassName, i, ln, alternate; if (!(alternates instanceof Array)) { alternates = [alternates]; } for (i = 0, ln = alternates.length; i < ln; i++) { alternate = alternates[i]; //<debug error> if (typeof alternate !== 'string') { Ext.Error.raise({ sourceClass: "Ext", sourceMethod: "define", msg: "Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string" }); } //</debug> this.set(alternate, cls); } });
uses后置处理器的定义如下:
Manager.registerPostprocessor('uses', function(name, cls, data) { var uses = Ext.Array.from(data.uses), items = [], i, ln, item; for (i = 0, ln = uses.length; i < ln; i++) { item = uses[i]; if (typeof item === 'string') { items.push(item); } } Loader.addOptionalRequires(items); });
所有上述的前置与后置处理器被调用结束后,就会将新创建的类注册到ClassManager中,代码如下:
set: function(name, value) { var targetName = this.getName(value); this.classes[name] = this.setNamespace(name, value); if (targetName && targetName !== name) { this.maps.alternateToName[name] = targetName; } return this; }
至此,整个类的创建便完成了。
相关推荐
extjs4.0开发人员以及学习可以下载参考
[09]EXTJS4.0的core包和Ext类.003.zip (60.22M)[09]EXTJS4.0的core包和Ext类.002.zip [09]EXTJS4.0的core包和Ext类.001.zip 第十讲:extjs4.0的util包 [10]EXTJS4.0的util包.001.zip (80.00M)[10]EXTJS4.0的...
#### 第九讲:Extjs 4.0的core包和Ext类 - **core包和Ext类**: - core包包含了一系列核心工具类和实用函数。 - Ext类是Extjs中的基础类,提供了许多常用的功能。 - **深入解析**: - 详细介绍core包中的关键类和...
ExtJS 4.0是Sencha公司开发的一款强大的JavaScript前端框架,主要用于构建富客户端Web应用程序。这个官方版本的发布标志着ExtJS在功能、性能和可维护性方面的一个重要里程碑。以下将详细介绍ExtJS 4.0中的核心概念、...
在“ExtJS4.0-WEB开发项目源代码”这个资源中,你将有机会接触到基于ExtJS 4.0版本的项目实例,这对于学习和理解ExtJS的应用开发是非常有价值的。 首先,让我们深入了解ExtJS 4.0的关键特性: 1. **组件化**:...
这个"EXTJS4.0开发手册源码"包含了EXTJS4.0框架的源代码,以及与其配套的开发指南,是深入理解EXTJS4.0内部机制和进行实际项目开发的重要参考资料。 EXTJS4.0的核心特性包括组件化开发、数据绑定、可扩展性、丰富的...
ExtJS4.0-API Ext4.0-API Ext4 ExtJS4 API 学EXTJS4的好东西...
这个视频教程配套代码主要涉及EXTJS4.0中的action类,这是EXTJS中处理服务器端交互的关键部分。在EXTJS中,action类通常指的是Ext.Ajax或Ext.data.proxy.Ajax,它们负责发送异步请求到服务器并处理响应。 在EXTJS...
EXTJS4.0视频教程配套源代码(1--30)
此外,ExtJS 4.0的可扩展性和模块化设计也使得它在大型项目中表现出色,能够轻松地管理和维护代码。 总的来说,这些文档是学习和掌握ExtJS 4.0不可或缺的资源,无论你是初学者还是经验丰富的开发者,都能从中...
### EXTJS 4.0 视频教程 30集 关键知识点解析 #### 一、EXTJS 4.0 概述与安装配置 **1.1 EXTJS 4.0简介** EXTJS 4.0是一款基于JavaScript的开源前端框架,用于构建交互式的Web应用程序。它提供了一套丰富的UI组件...
Extjs 4.0中文版API
extjs4.0教程源代码,精品分享
EXTJS 4.0 是一个面向开发 RIA 的 AJAX 应用,是一个用 JavaScript 写的,主要用于创建前端用户界面,是一个与后台技术无关的前端 AJAX 框架。它能够帮助我们在页面上快速而简单构建各种各样的控件,简化我们自己去...
extjs4.0手册翻译 0-0-入门.doc 0-1-Ext 4概述.doc 1-1-类系统.doc 1-2-MVC应用架构.doc 1-3-布局和容器.doc 1-4-组件.doc 1-6-拖放.doc 1-7-本地化.doc 1-8-键盘导航.doc 2-1-表格.doc 2-2-树.doc 2-3-数据.doc 2-4...
- 编写简单的JavaScript代码,使用Extjs4.0 API创建一个Panel组件。 2. **创建 `helloWorld.html`**: - 引入CSS和JS文件,并确保路径正确。 - 浏览器访问该页面,检查是否正确显示Panel组件。 3. **使用 `Ext....
总结一下,`Extjs4.0代码补全jsb文件`是提高基于ExtJS 4.0.0开发效率的关键工具,它为IDE提供了必要的元数据,实现了代码补全和自动提示功能,帮助开发者更好地理解和使用ExtJS库,减少错误,提高开发速度。...
4. **后台管理系统**:这类系统通常包括用户管理、权限控制、数据报表、工作流等模块,用于企业内部的日常运营和管理。本项目应包含这些基本功能,且具备良好的可扩展性,以适应不同企业的业务需求。 5. **可二次...
ExtJs4.0入门教程,详细介绍ExtJs4.0。