手工打造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[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>


csdn不可以上传附件 下载代码下面的链接

http://junjun16818.iteye.com/admin/blogs/1733447



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值