最近看了几本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[key]){ c1[key] = c2[key]; } } } }, /* 复制对象的属性*/ apply: function(c1, c2) { if(typeof c1 == "object" && typeof c2 == "object"){ for(var key in c2) { c1[key] = c2[key]; } } 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 name var v = name.split('.'), o = window[v[0]], i = 1, len = v.length; for(; i < len ; i++) { if(!o) return o o = o[v[i]] ; } return o; }, /* 定义名字空间 */ namespace: function(name) { if(typeof name !== "string") return name var v = name.split('.'), o = window[v[0]] = window[v[0]] || {}, i = 1, len = v.length; for(; i < len ; i++) { o = o[v[i]] = o[v[i]] || {}; } 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[i]); } } /* 检测接口方法是否被实现 */ Interface.ensureImplements = function(object) { var i = 1, len = arguments.length, j = 0, menthodsLen; for (; i < len; i++) { for (methodsLen = arguments[i].methods.length; j < methodsLen; j++) { var method = arguments[i].methods[j]; if (!object[method] || typeof object[method] !== 'function') { throw new Error("类" + object.className + "未实现" + arguments[i].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[key] === method) { methodName = key } } superClass.prototype[methodName].apply(this,cfg) } } /* 添加别名到 widgetMap 对象中 */ function setWidget(name,cls) { var n = name; if (name.indexOf('widget.') > -1) { n = name.substring(7,name.length); } widgetMap[n] = cls; } var pub = { /* 使用字符串格式名称创建类 */ create: function(name, cfg) { var clazz = util.getClass(name); return new clazz(cfg); }, /* 使用别名创建一个类 */ widget: function(name, cfg) { var clazz = widgetMap[name]; 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[lastName] = new Interface(name, cfg.methods); return; } /* 创建对象 cfg.constructor 为构造函数 */ if (cfg.constructor !== Object) { thatClass = thatClass[lastName] = cfg.constructor; delete cfg.constructor; } else { thatClass = thatClass[lastName] = 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 = [implement] } var i = 0, len = implement.length; for (; i < len; i++) { if (typeof implement[i] == 'string') { Interface.ensureImplements(thatClass.prototype,util.ns(implement[i])); } else if (typeof implement[i] == 'object') { Interface.ensureImplements(thatClass.prototype,implement[i]); } 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[i]; if(!cfg[method] || typeof cfg[method] !== '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 = [urls]; } var url, count = 0, i = 0, len = urls.length; for (; i < len; i++) { (function(i){ count++ ; url = urls[i]; 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[i])) { 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')[0].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[i], 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>