最近看了几本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>