junjun16818 发表于 2013-1-22 21:47:58

手工打造Extjs (1) 类系统

最近看了几本javascrpt方面的书(javascript高级程序设计,javascript设计模式),内容中讲到很多面向对象式开发也提到很多优秀的javascript框架,
我很是费解为什么就没有提到过Extjs,难道YUI跟Extjs有那么大得深仇大恨?。


转入正题,我特别喜欢Extjs4 的类体系,所以就自己模仿的写了一个,我只是简单了解过extjs并没有深入去学习,所以有些地方可能不对还望大家指出。


在我的类体系中可以声明接口、抽象类、类,声明方式与extjs类似


下面是Extjs的类声明
Ext.define('Ext.panel.Panel', {
extend: 'Ext.panel.AbstractPanel'
// .....
})

下面是代码 :ClassManager.js



var Design = Design || {};(function(Class) {/*先定义一些工具方法*/var widgetMap = {};var util = {/* 复制对象的属性,如果他们不存在 */applyIf: function(c1, c2) {if(typeof c1 == "object" && typeof c2 == "object"){for(var key in c2) {if(!c1){c1 = c2;}}}},/* 复制对象的属性*/apply: function(c1, c2) {if(typeof c1 == "object" && typeof c2 == "object"){for(var key in c2) {c1 = c2;}}return c1;},isObject: function(o) {return (typeof o == "object");},isArray: function(o) {return (o instanceof Array);},isFunction: function(o) {return (typeof o == "function");},isString: function(o) {return (typeof o == "string");},isNumber: function(o) {return (typeof o == "number");},/*根据字符串类型获取一个类*/getClass: function(name) {if(typeof name !== "string")return namevar v = name.split('.'),o = window],i = 1,len = v.length;for(; i < len ; i++) {if(!o) return oo = o] ;}return o;},/* 定义名字空间 */namespace: function(name) {if(typeof name !== "string")return namevar v = name.split('.'),o = window] = window] || {},i = 1,len = v.length;for(; i < len ; i++) {o = o] = o] || {};}return o;},ns: function(){return this.namespace.apply(this,arguments);}}/*接口类下面接口类是设计模式书中的一个例子 copy过来的*/var Interface = function(name, methods) {var i = 0,len = methods.length;this.name = name;this.methods = [];for (; i < len; i++) {this.methods.push(methods);}}/*检测接口方法是否被实现*/Interface.ensureImplements = function(object) {var i = 1,len = arguments.length,j = 0,menthodsLen;for (; i < len; i++) {for (methodsLen = arguments.methods.length; j < methodsLen; j++) {var method = arguments.methods;if (!object || typeof object !== 'function') {throw new Error("类" + object.className + "未实现" + arguments.name + " 接口方法 " + method);}}}}/*类式继承*/function extend(subClass,superClass) {var F = function(){};F.prototype = superClass.prototype;subClass.prototype = new F();util.applyIf(subClass.prototype,new F())subClass.prototype.constructor = subClass;if (superClass.prototype.constructor == Object.prototype.constructor) {superClass.prototype.constructor = superClass;}/*为子类添加静态属性 superclass 指向父类*/subClass.superclass = superClass.prototype;/*为子类添加一个方法 callParent 调用父类的当前函数*/subClass.prototype.callParent = function(cfg) {var method,methodName;method = this.callParent.caller;for (var key in this) {if (this === method) {methodName = key}}superClass.prototype.apply(this,cfg)}}/*添加别名到 widgetMap 对象中*/function setWidget(name,cls) {var n = name;if (name.indexOf('widget.') > -1) {n = name.substring(7,name.length);}widgetMap = cls;}var pub = {/*使用字符串格式名称创建类*/create: function(name, cfg) {var clazz = util.getClass(name);return new clazz(cfg);},/*使用别名创建一个类*/widget: function(name, cfg) {var clazz = widgetMap;return new clazz(cfg);},/*声明一个类*/define: function(name ,cfg) {/*获取当前字符串name的变量*/var nameArr = ('window.'+name).split('.'),lastName = nameArr.pop(),beginName = nameArr.join('.'),thatClass = util.ns(beginName),parentClass = util.ns(cfg.extend),implement = cfg.implement;/*当前类型为接口时 创建接口并返回*/if (cfg.classType == "interface") {thatClass = new Interface(name, cfg.methods);return;}/*创建对象 cfg.constructor 为构造函数*/if (cfg.constructor !== Object) {thatClass = thatClass = cfg.constructor;delete cfg.constructor;}else {thatClass = thatClass = function() {if (parentClass) {parentClass.prototype.constructor.apply(this,arguments)}};}/*当cfg里配置了父类时继承*/if (parentClass) {if (parentClass.prototype.events && cfg.events) {util.applyIf(cfg.events,parentClass.prototype.events);}extend(thatClass,parentClass);}util.apply(thatClass.prototype, cfg);thatClass.prototype.className = name;/*当配置了接口时,检查是否有没有实现的方法,如有没实现的方法将会抛出异常*/if (implement) {if (!util.isArray(implement)) {implement = }var i = 0,len = implement.length;for (; i < len; i++) {if (typeof implement == 'string') {Interface.ensureImplements(thatClass.prototype,util.ns(implement));}else if (typeof implement == 'object') {Interface.ensureImplements(thatClass.prototype,implement);}else {throw new Error("interface error implements type string and object");}}}/*当父类为抽象类时检测是否有为实现的方法*/if (parentClass && parentClass.classType == "abstract") {for (var i = 0, len = parentClass.methods.length; i < len; i++) {var method = parentClass.methods;if(!cfg || typeof cfg !== 'function'){throw new Error(name+" 未实现 "+ method +" 方法, 抽象类" + parentClass.className);}}}/*当前类为抽象类时添加几个静态属性 作为检测时使用*/if (cfg.classType == "abstract") {thatClass.classType = "abstract";thatClass.methods = cfg.methods;}/*当配置alias属性为当前类配置别名调用setWidget(name,class) 将别名与当前类添加到map中 */if (cfg.alias) {if (util.isArray(cfg.alias)) {cfg.alias.forEach(function(it) {if (util.isString(it)) {setWidget(it, thatClass);}else {throw new Error("define argument 'alias' type Error");}});}else if (util.isString(cfg.alias)) {setWidget(cfg.alias, thatClass);}else {throw new Error("define argument 'alias' type Error");}}}}util.applyIf(Class,pub);util.applyIf(Class,util);})(Design);
html 页面


<script src = "ClassManager.js" ></script><script>Design.define('Design.interface.Component', {classType: 'interface',methods: ['initComponent', 'getItems']});Design.define('Design.panel.AbstractPanel', {classType: 'abstract',getElement: function() {return Document.createElement('div');},methods: ['getValue', 'setValue']});Design.define('Design.panel.Panel', {alias: 'panel', /*别名*/extend: 'Design.panel.AbstractPanel',/* 继承抽象类 */implement: 'Design.interface.Component',/* 继承接口 */constructor: function(cfg) { /*构造函数*/this.name = cfg.name;},getName: function() { /*类内方法*/return this.name;},initComponent: function(){ /* 必须实现的接口方法 没有这个方法将会抛出异常 */},getItems: function() { /* 必须实现的接口方法 没有这个方法将会抛出异常 */},getValue: function() { /* 必须实现的抽象方法 没有这个方法将会抛出异常 */},setValue: function() { /* 必须实现的抽象方法 没有这个方法将会抛出异常 */}});/* 创建Panel类 */var panel = new Design.panel.Panel({name: 'zwl'});/* 使用字符串类名创建 */panel = Design.create('Design.panel.Panel',{name: 'zwl'});/* 使用别名创建 */panel = Design.widget('panel',{name: 'zwl'});alert(panel.getName());</script>

现在这个类系统已经完成  上面是一个测试的例子。现在还有一个问题,如果把Design.interface.Component 、 Design.panel.AbstractPanel Design.panel.Panel 分离出到js文件,
我们还需要在页面导入下面这几个文件.
<script src = "app/interface/Component.js" ></script>
<script src = "app/panel/AbstractPanel.js" ></script>
<script src = "app/panel/Panel.js" ></script>


如果类的数量一旦多起来,将要导入很多个js文件。这样的话 我的类系统太弱了,接下来添加一个动态导入js文件的功能


创建一个名叫Loader的js文件 代码如下
(function(Class) {/* 文件路径与转换名 */var path = {name: 'Design', replaceName: ''};/**/var requireCount = 0;var addEvent = function(el,type, fn) {if (window.addEventListener){el.addEventListener(type, fn, false);}else if (window.attachEvent) {el.attachEvent('on' + type, fn);}}Class.onReady = function(callback) {addEvent(window, 'load', function(){/*每隔一段时间会判断是否还在加载类文件 在加载完成后调用回调*/var intervalId = setInterval(function(){if(requireCount === 0) {clearInterval(intervalId);callback();}},5)});};/* 导入类方法 */var require = function(urls, onLoad) {if (!Class.isArray(urls)) {urls = ;}var url,count = 0,i = 0,len = urls.length;for (; i < len; i++) {(function(i){count++ ;url = urls;requireCount++;if (url.indexOf(path.name) === 0) {url = path.replaceName + url.substring(path.name.length, url.length);}var script = document.createElement('script');script.type = 'text/javascript';script.src = url.replace(/\./g,'/')+'.js';script.onload = function() {count--;/*每隔一段时间会判断这个文件中得类是否已经声明完 如对象存在再调用回调*/var intervalId = setInterval(function(){if (Class.getClass(urls)) {requireCount--;clearInterval(intervalId);if (count == 0 && onLoad) {onLoad.call(window);}}},5)};script.onerror = function() {throw new Error(" require Errors url = " + url);};script.onreadystatechange = function() {if (this.readyState === 'loaded' || this.readyState === 'complete') {}};document.getElementsByTagName('head').appendChild(script);})(i)}};var pub = {    /*设置路径*/setPath: function(o, n) {path.name = o;path.replaceName = n;}}Class.Loader = pub;Class.require = require;})(Design); 
最后在define方法最前面添加一段代码


var pub = {define: function(name ,cfg) {/*如果父类与接口不存在将阻止声明类先加载父类与接口加载完成后再声明类*/var requireList = [];if (cfg.extend && !util.getClass(cfg.extend)) {requireList.push(cfg.extend);}if (cfg.implement && !util.getClass(cfg.implement)) {requireList = requireList.concat(cfg.implement);}var loaderCount = requireList.length;for (var i = 0, len = requireList.length; i < len; i++) {Class.require(requireList, function() {loaderCount--;if (loaderCount == 0) {pub.define(name, cfg);}});}if (requireList.length) {return;}//........}}




html 页面


<script src = "ClassManager.js" ></script><script src = "Loader.js" ></script><script>Design.Loader.setPath('Design', 'apps');// 配置路径 Design.panel.Panel 会转换成 apps/panel/Panel.js 既js文件的路径Design.require(['Design.panel.Panel']); // 在onReady之前会 加载3个js文件Design.onReady(function(){/* 使用别名创建 */panel = Design.widget('panel',{name: 'zwl'});alert(panel.getName());});</script> 
 
页: [1]
查看完整版本: 手工打造Extjs (1) 类系统