Javascript面向对象扩展库最近一直在用js做项目,遇到了许多需要应用面向对象来设计的功能,由于js对OOP的原生支持还不是很完善,所以就写了一个面向对象的扩展库用做底层支持,现在把它单独整理出来,完善了一些功能,在这里分享一下。 lang.js库提供了包和类的定义、类的继承与混合(mixin)、函数重载等功能,基本可满足大多数面向对象设计的需求。同时支持基于链式的定义方式,让库在使用时更加规范和便捷。下面首先通过简单的例子演示了lang.js的基本功能,之后给出了lang.js的源码及注释。
一.功能介绍 “lang”作为框架的全局定义,其中包括了四个方法: lang.Package(string name) //用于定义包(默认会暴露到全局) lang.Class(string name[, object config], object classBody) //用于定义类 lang.Object(string name | object body) //用于定义支持重载函数的普通对象 lang.Function(string name | object body) //用于定义重载函数
//1.基本的包和类的定义: //首先通过全局函数Package定义一个包,下面可通过链式调用向包中定义类、对象或函数。 Package('com.site'). Class('Base',{ Base: function(tagName){ this.element = document.createElement(tagName); }, add: function(child){ this.element.appendChild(child); }, show: function(){ alert('I am Base!'); } }). Class('Control', { Control: function(){ //do soming } }); alert(com.site); alert(com.site.Base); alert(com.site.Control); //2.普通对象定义 //一个不需初始化的类,可直接定义成普通对象 Package('com.site.util'). Object({ ModelA: { say: function(){ alert('ModelA: Hello!'); } }, ModelB: { print: function(){ alert('ModelB: ' + this.Class.Type.name); } } }); com.site.util.ModelA.say(); //3.类的继承 //像很多面向对象语言一样,这里约定一个类只能拥有一个父类。但是可以通过mixin混合多个普通对象。 //在子类的构造函数中可以通过this.base访问父类的构造函数。 //如子类定义了与父类相同函数,父类函数将被重写,这里同样可以通过this.base调用父类被重写的函数。 Package('com.site'). Class('Panel', {extend: com.site.Base, mixin:[com.site.util.ModelA, com.site.util.ModelB]}, { Panel: function(tagName){ //这里使用this.base调用父类构造函数 this.base(tagName); }, show: function(){ alert('I am Panel!'); this.base(); } }); var panel = new com.site.Panel("div"); panel.say(); panel.print(); alert(panel.element.outerHTML); //4.静态成员的定义 /* 程序中可以将类的主体以函数的形式定义,将私有成员定义在函数内部,将公共成员以返回值的形式暴露出来。这种设计模式在很多流行框架中都可以看到,虽然隐藏的成员只能是静态的,不过这也足以满足大部分的需求了。 公有的静态成员可以通过Static标签定义,Static中可包含一个静态构造函数,该函数只会在类第一次创建的时候执行一次。 在运行期间类的内部可以通过this.Class.xxx的形式访问自身的静态成员,(this.Class就是所属类的引用) */ Package('com.site'). Class('Button', {extend: com.site.Panel}, function(){ //在这里定义私有静态成员 var tagName = 'BUTTON'; var onclick = function(){ alert('I am a Button!'); } return { //这里定义共有静态成员 Static: { count: 1, //静态构造函数 Button: function(){ this.createTime = new Date(); } }, Button: function(text){ this.base(tagName); this.element.innerHTML = text; this.element.onclick = onclick; //调用静态变量 this.Class.count++; } }; }); var button = new com.site.Button("Button1"); document.body.appendChild(button.element); alert(com.site.Button.count); //5.重载函数的定义 /* lang.js中对函数重载提供了较完善的支持,在定义函数时可在函数名后面指定参数签名,调用函数时程序根据传入的参数自动匹配转发。 其中支持的类型包括:var,string,number,boolean,array,date,object,function,element和自定义的类(var表示任意类型,element表示DOM元素。) 为了保证灵活性,重载支持通配符功能,遵循正则表达式的规则,可以通过标记“+ * ? {?,?}”来处理不确定参数个数的情况。 重载函数在类的继承过程中是有效的,签名不同的函数不会被重写,被重写的父类重载函数同样支持通过this.base调用。 以下代码为重载在类中应用的例子: */ Package('com.site'). Class('Student',{ Static: { //静态函数重载 'create()': function(){ //这里的this就是类本身,所以可以直接new return new this('静态', 1); }, 'create(string, number)': function(name, age){ return new this(name, age); }, 'search()': function(){ alert('search(*)'); }, 'search(string)': function(name){ alert('search('+name+')'); } }, //构造函数重载 'Student()': function(){ //利用this.self调用自身构造函数"Student(string, number)" this.self('未知', 0); }, 'Student(string, number)': function(name, age){ this.name = name; this.age = age; }, //普通成员函数重载 'print()': function(){ this.print(this.name ,this.age); }, 'print(string, number)': function(name, age){ alert('name:' + name + ',age:' + age); }, //支持自定义类的类型 'print(lib.ui.Panel)': function(panel){ this.print(panel, true); }, 'print(lib.ui.Panel, boolean)': function(panel, append){ if(append){ panel.element.innerHTML += this.name; }else{ panel.element.innerHTML = this.name; } } }). //继承中对重载的支持 Class('MaleStudent',{extend: com.site.Student},{ Static: { //重写父类静态函数 'search(string)': function(name){ alert('MaleStudent: search('+name+')'); }, 'search(string, number)': function(name, age){ alert('MaleStudent: search('+name+','+age+')'); } }, 'MaleStudent': function(){ //调用父类的构造函数"Student(string, number)" this.base('未知男同学', 0); }, 'MaleStudent(string, number)': function(name, age){ //调用父类的构造函数"Student(string, number)" this.base(name, age); }, //重写了父类的重载函数 'print()': function(){ alert('MaleStudent: name:' + this.name + ',age:' + this.age); }, 'print(string, number)': function(name, age){ //通过this.base调用父类函数"print(string, number)" this.base('<'+name+'>', '<' + age + '>'); }, //模糊匹配,这个函数可接受一个或多个字符串和零个或多个数字 'print(string+, number*)': function(){ var msg = []; for(var i=0; i<arguments.length; i++){ msg.push(arguments[i]); } alert('print(string+, number*):' + msg.join('+')); } }); //创建对象时会自动判断调用的构造函数 var studentA = new com.site.Student(); studentA.print(); var studentB = new com.site.MaleStudent('王小虎', 19); studentB.print(); studentB.print('小虎', 19); //这里调用了模糊匹配的重载函数 studentB.print('a','b','c'); studentB.print('x','y','z', 1, 2 ,3); var studentC = new com.site.MaleStudent(); studentC.print(); //调用静态重载函数 var student = com.site.Student.create(); student.print(); com.site.Student.search(); com.site.Student.search('某同学'); //共有的静态函数会继承到子类,所以MaleStudent也可以create var maleStudent = com.site.MaleStudent.create('小红', 18); maleStudent.print(); com.site.MaleStudent.search('小明'); com.site.MaleStudent.search('小明',20); //6.匿名定义 //有些时候,我们不希望显示的指定一个类或函数的名字,只希望把它保存到变量里面,那么可以通过匿名的方式进行定义。 //1)使用lang.Class创建匿名类 var clazz = lang.Class({ //匿名类的名字统一为Anonymous,所以构造函数的名字也要是Anonymous Anonymous: function(name){ this.name = name; }, show: function(){ alert(this.name); } }); alert(clazz); new clazz('匿名类').show(); //2)使用lang.Function创建匿名重载函数 var fn = lang.Function({ '(default)': function(){ var a = [].slice.call(arguments, 0); alert(a); }, '(string)': function(a){ alert('(string): ' + a); }, '(string, number)': function(a, b){ alert('(string, number): ' + a + ':' + b); }, '(number)': function(a){ alert('(number): ' + a); } }); alert(fn); fn('message'); fn('message', 1); fn(1); fn(1,2,3,4,5); //3)使用lang.Object创建支持重载函数的普通对象 var obj = lang.Object({ name: 'jim', 'write()': function(){ this.write(this.name); }, 'write(string)': function(name){ alert(name); } }); obj.write(); obj.write('joe'); //7.综合例子 Package("com.site"). Class('ClassA', { print: function(){ alert(this.toString()); } }). Class('ClassB', { print: function(){ alert(this.toString()); } }). Object({ fn1: function(){ alert(1); }, fn2: function(){ alert(2); }, 'debug(string)': function(msg){ alert(msg); }, 'debug(string, number)': function(msg, num){ alert(num + ': ' + msg); }, 'debug(com.site.ClassA, string)': function(c, method){ alert('debug ClassA'); c[method](); }, 'debug(com.site.ClassB, string)': function(c, method){ alert('debug ClassB'); c[method](); }, 'debug(element)': function(element){ alert('element:' + element.outerHTML); } }). Object('Web', { 'reload': function(){ alert('reload...'); }, 'reload(function)': function(fn){ fn(); } }). Package('com.site.util'). Function('myfn(string)', function(name){ alert(name); }). Function('myfn(number)', function(num){ alert(num*num); }). Class('ClassA', { ClassA: function(){ alert('ClassA!'); } }); var div = document.createElement("div"); var span = document.createElement("span"); div.appendChild(span); com.site.fn1(); com.site.fn2(); com.site.debug('info'); com.site.debug('info', 1); com.site.debug(new com.site.ClassA(), 'print'); com.site.debug(new com.site.ClassB(), 'print'); com.site.debug(div); com.site.Web.reload(); com.site.Web.reload(function(){alert('callback');}); com.site.util.myfn('Jim'); com.site.util.myfn(100); new com.site.util.ClassA(); //8.自描述信息 /* 自描述就是类或对象在定义完成后对自身的信息的描述,常常用于实现一些特殊的功能,库中通过Type"保留字"存储类或包的自描述信息。 下面为使用方法: */ var value = com.site.Student; alert('判断是否一个类:' + (value.Type && value.Type.type == 'class')); alert('取完整类名:' + com.site.Student.Type.name); alert('取父类完整类名:' + com.site.MaleStudent.Type.baseClass.Type.name); alert('取短类名:' + com.site.Student.Type.shortName); alert('取一个类的包名:' + com.site.Student.Type.Package.Type.name); 二.源码详解 var lang = (function(){ /*********************************** Javascript面向对象扩展库(lang.js v1.0) By: X!ao_f QQ: 120000512 Mail: xiao_f.mail#163.com ************************************/ var customToString = function(){ return '[' + this.Type.type + ' ' + this.Type.name + ']'; } //支持重载的方法定义 var createMethod = (function(){ //创建一个代理函数 var createMethodProxy = function(context, name){ //当调用重载的函数时,首先会执行该函数分析传入的参数,进行匹配和转发 var method = function(){ //在第一次调用时初始化,将映射信息缓存 if(!method.__initialized__){ initializeMethod(method); } //将参数类型拼接成函数签名 var signature; if(arguments.length){ var list = []; for(var i=0; i<arguments.length; i++){ var typename; var argument = arguments[i]; if(argument === undefined || argument === null){ typename = 'object'; }else if(argument instanceof Array){ typename = 'array'; }else if(argument instanceof Date){ typename = 'date'; }else{ typename = typeof argument; if(typename == 'object'){ if('Class' in argument){ typename = argument.Class.Type.name; }else if('nodeType' in argument){ typename = 'element'; } } } list.push(typename); } signature = list.join(','); }else{ signature = ''; } //如果常规缓存中存在匹配的签名,直接调用 if(method.__overloads__[signature]){ return method.__overloads__[signature].apply(this, arguments); }else{ //缓存中不存在时,尝试利用正则进行模糊匹配 //首先判断模糊匹配缓存中是否存在记录,如存在直接调用 if(method.__overloadsCache__[signature]){ return method.__overloadsCache__[signature].apply(this, arguments); } //循环匹配 for(var i=0; i<method.__overloadsRegExp__.length; i++){ //如果匹配成功,将映射关系存入模糊匹配缓存,同时调用并返回 if(method.__overloadsRegExp__[i].regexp.test(signature)){ method.__overloadsCache__[signature] = method.__overloadsRegExp__[i].fn; return method.__overloadsRegExp__[i].fn.apply(this, arguments); } } //如果依然无法找到对应的函数,判断是否存在默认函数 if(method.__overloads__['default']){ return method.__overloads__['default'].apply(this, arguments); }else if(method.__overloads__['']){ return method.__overloads__[''].apply(this, arguments); }else{ alert('Error: '+method.Type.name+'('+signature+') is undefined.'); } } }; //内置对象 method.__context__ = context; method.__functions__ = {}; method.toString = customToString; //自描述信息 method.Type = { name: name, Method: method, type: 'method' }; return method; } //初始化 var initializeMethod = function(method){ //基础签名缓存 method.__overloads__ = {}; //模糊匹配正则缓存 method.__overloadsRegExp__ = []; //模糊匹配结果缓存 method.__overloadsCache__ = {}; //例举所有定义的函数 for(var signature in method.__functions__){ var fn = method.__functions__[signature]; var params = signature.substring(signature.indexOf('(') + 1, signature.length - 1); var pure = !/[\*\+\?\{]/.test(params); //如果不存在通配符直接保存到基础签名缓存 if(pure){ method.__overloads__[params] = fn; }else{ //生成模糊匹配正则 var regexp = '^' + params .replace(/([\w\.]+)(\{.*?\})?/g, '($1(,|$))$2') .replace(/\./g, '\\.') .replace(/((\()var(\())/g, '$2\\w+$3') .replace(/,\(/g, '(') + '$'; method.__overloadsRegExp__.push({ regexp: new RegExp(regexp), fn: fn }); } } method.__initialized__ = true; } //返回外部的定义函数 return function(signature, fn, comp){ //如果传入的为一个对象,视为定义匿名方法 if(typeof signature == 'object'){ var context = {}; var method; for(var key in signature){ method = createMethod.call(context, 'anonymous'+key, signature[key]); } return method; } signature = signature.replace(/\s+/g, ''); var index = signature.indexOf('('); var name = index > -1 ? signature.substring(0, signature.indexOf('(')) : signature; var context = this; var method = context[name]; //上下文中不存在函数定义,视为第一次定义 if(method === undefined){ context[name] = method = createMethodProxy(context, name); }else if(!method.Type || method.Type.type!='method'){ //上下文存在的函数是原生函数,将这个函数作为默认函数存入列表 var temp = method; context[name] = method = createMethodProxy(context, name); method.__functions__[name + '()'] = temp; }else{ //如果上下文不同,创建新的重载方法并将已经存在的函数复制,这里主要解决类继承中子类与父类冲突的问题 //如果上下文相同,直接将初始化标记设为false,待下次调用时重新初始化 if(method.__context__ !== context){ var temp = method; context[name] = method = createMethodProxy(context); for(var sign in temp.__functions__){ method.__functions__[sign] = temp.__functions__[sign]; } }else{ method.__initialized__ = false; } } //将本次定义的函数添加到函数列表 //先入为主策略 if(comp){ if(fn.__functions__){ for(var key in fn.__functions__){ if(key in method.__functions__){ method.__functions__[key].__overridden__ = fn; }else{ method.__functions__[key] = fn; } } }else{ if(signature in method.__functions__){ method.__functions__[signature].__overridden__ = fn; }else{ method.__functions__[signature] = fn; } } }else{ //后入为主策略 if(fn.__functions__){ for(var key in fn.__functions__){ if(key in method.__functions__){ fn.__functions__[key].__overridden__ = method; } method.__functions__[key] = fn.__functions__[key]; } }else{ if(signature in method.__functions__){ fn.__overridden__ = method; } method.__functions__[signature] = fn; } } if(this.Type && this.Type.type == 'package'){ return this; }else{ return method; } }; })(); //类定义函数 var createClass = (function(){ var slice = Array.prototype.slice; var emptyFn = function(){}; var createClass = function(name){ return function(){ this[name].apply(this, slice.call(arguments, 0)); }; } //用于调用被重写函数 var baseCaller = function(){ if(arguments.length){ var args = slice.call(arguments, 0); return baseCaller.caller.__overridden__.apply(this, args); }else{ return baseCaller.caller.__overridden__.call(this); } } //用于调用自身重载构造函数 var selfCaller = function(){ if(arguments.length){ var args = slice.call(arguments, 0); return selfCaller.caller.__self__.apply(this, args); }else{ return selfCaller.caller.__self__.call(this); } } var filter = {prototype:true, Type:true}; //快速浅拷贝 function clone(a){ var fn = function(){}; fn.prototype = a; return new fn; } //对象复制,替换存在的(后入为主) function replace(base, self){ for(var key in self){ if(!(key in filter)){ if(typeof self[key] == 'function'){ //如果子类函数包含重载签名或父类函数已经重载 if(key.indexOf('(') > -1 || (base[key] && base[key].__functions__)){ createMethod.call(base, key, self[key]); }else{ //常规函数定义 if(key in base){ //记录重写信息 self[key].__overridden__ = base[key]; } base[key] = self[key]; } }else{ base[key] = self[key]; } } } } //对象复制,只取补集(先入为主) function complement(self, base){ for(var key in base){ if(!(key in filter)){ if(typeof base[key] == 'function'){ if(key.indexOf('(') > -1 || (self[key] && self[key].__functions__)){ createMethod.call(self, key, base[key], true); }else{ if(key in self){ //记录重写信息 self[key].__overridden__ = base[key]; }else{ self[key] = base[key]; } } }else if(!(key in self)){ self[key] = base[key]; } } } } return function(){ //处理参数 if(this.Type && this.Type.type == 'package'){ if(arguments.length == 2){ var name = arguments[0]; var body = arguments[1]; }else{ var name = arguments[0]; var config = arguments[1]; var body = arguments[2]; } }else{ if(arguments.length == 1){ var name = 'Anonymous'; var body = arguments[0]; }else{ var name = 'Anonymous'; var config = arguments[0]; var body = arguments[1]; } } //创建类的基础函数 var clazz = createClass(name); //获取父类信息 var baseClass; if(config && config.extend){ baseClass = config.extend; } //如果传入的主体为函数,取其返回值 if(typeof body == 'function'){ body = body(clazz); } //处理静态成员 if(body.Static){ complement(clazz, body.Static); delete body.Static; body = body.Public||body; }else{ body = body.Public||body; } //处理继承 if(baseClass){ //通过快速浅拷贝复制父类成员 clazz.prototype = clone(baseClass.prototype); //继承静态成员 complement(clazz, baseClass); //继承类成员 complement(clazz.prototype, body); }else{ //不存在继承 clazz.prototype = {}; complement(clazz.prototype, body); } //处理混合 if(config && config.mixin){ var mixin = config.mixin; if(mixin instanceof Array){ for(var i=0; i<mixin.length; i++){ replace(clazz.prototype, mixin[i]); } }else{ replace(clazz.prototype, mixin); } } //添加内置函数 clazz.prototype.base = baseCaller; clazz.prototype.self = selfCaller; clazz.prototype.constructor = clazz; clazz.prototype.toString = customToString; clazz.toString = customToString; clazz.prototype.Class = clazz; if(clazz.prototype[name]){ var constructor = clazz.prototype[name]; if(constructor.__functions__){ for(var key in constructor.__functions__){ //存在重载时,添加自身引用,用于通过this.self调用重载构造函数 constructor.__functions__[key].__self__ = constructor; //存在继承时,将父类的构造函数作为被重写的函数,配置给当前类的构造函数 //用于通过base调用父类构造函数 if(baseClass){ constructor.__functions__[key].__overridden__ = baseClass.prototype[baseClass.Type.shortName]; } } }else if(baseClass){ clazz.prototype[name].__overridden__ = baseClass.prototype[baseClass.Type.shortName]; } }else{ clazz.prototype[name] = emptyFn; } //类型自描述信息 //如果当前上下文是一个包,将类添加到包中 if(this.Type && this.Type.type == 'package'){ clazz.Type = { type:'class', name: this.Type.name+'.'+name, shortName: name, Package: this, Class: clazz, baseClass: baseClass } clazz.prototype.Type = { type: 'object', name: this.Type.name+'.'+name } //将类添加到包 this[name] = clazz; //调用静态构造函数 if(name in clazz){ clazz[name].call(clazz); } //返回this用于链式调用 return this; }else{ //上下文不是包则直接返回 clazz.Type = { type:'class', name: name, shortName: name, Class: clazz, baseClass: baseClass } clazz.prototype.Type = { type: 'object', name: name, baseClass: baseClass } if(name in clazz){ clazz[name].call(clazz); } return clazz; } }; })(); //用于创建支持重载的普通对象 var createObject = function(objects, config){ var target; if(this.Type && this.Type.type == 'package'){ target = this; }else{ target = {}; } if(typeof objects == 'string'){ target = this[objects] = {}; objects = config; }else if(typeof objects == 'function'){ objects = objects(); } for(var key in objects){ if(typeof objects[key] == 'function' && (key.indexOf('(') > -1 || typeof target[key] == 'function')){ createMethod.call(target, key, objects[key]); }else{ target[key] = objects[key]; } } if(this.Type && this.Type.type == 'package'){ return this; }else{ return target; } }; //用于创建包 var createPackage = (function(){ var root = this; return function(package){ var name = []; var path = package.split('.'); var parent = root; for(var i=0; i<path.length; i++){ name.push(path[i]); if(parent[path[i]]){ parent = parent[path[i]]; }else{ var pack = { Class: createClass, Object: createObject, Function: createMethod, Package: createPackage, toString: customToString }; pack.Type = { type: 'package', Package: pack, name: name.join('.') } parent = parent[path[i]] = pack; } } return parent; } })(); //默认将Package暴露 window.Package = createPackage; return { Package: createPackage, Class: createClass, Function: createMethod, Object: createObject }; })(); 结束语: 到这里,lang.js的应用和原理就介绍完毕了,该库在主流浏览器中均已测试通过, 如果想使用lang.js,可以在这里免费下载,如发现什么问题,或有好的建议可以反馈给我。
标签: Javascript, 框架设计 |
|
来自: CevenCheng > 《Web前端》