偶然在infoq上看到朋友的文章《dojo类机制简介》,闲来无事,根据文章所讲,做了个dojo类机制的模拟实现。
主要实现以下功能:
定义类、定义类静态变量、实现单继承和多继承、调用父类方法 以及工具类方法和属性(isInstanceOf方法和declaredClass)。
代码如下:
//dojo类机制模拟实现 //--------工具类------------- //创建命名空间 function createNS(path){ if(!path)return null; var r ; if(typeof path == 'string') r = path.split('.'); else if(path.constructor==Array) r = path; var currentPath = window; for(var i=0;i<r.length;i++){ currentPath[r[i]] = currentPath[r[i]] || {}; currentPath = currentPath[r[i]]; } return currentPath; } //掺元继承 function mix(s,d){ for(var i in s){ if(s[i]&&s.hasOwnProperty&&s.hasOwnProperty(i)) d[i] = s[i]; } } //类式继承 function extend(subclass,superclass){ var F = function(){};/*在此使用一个空函数,并将用它创建的一个对象实例插入到原型链中,这样做可以避免创建超类的新实例.因为1.超类的示例可能会比较大2.超类的构造函数有一些副作用3.或者执行一些需要大量计算的任务*/ F.prototype = superclass.prototype; subclass.prototype = new F(); subclass.prototype.constructor = superclass; //为类和对象增加父类引用'superclass' subclass.prototype.superclass = subclass.superclass = superclass.prototype; //对使用“superclass.prototype={method1:function(){...},...}”方式定义的类进行构造器属性修正 if(superclass.prototype.constructor==Object.prototype.constructor){ superclass.prototype.constructor = superclass; } } //----------------------------------------------- //类机制实现:声明类 function declareClass(path,parent,config){ if(!path) return; var declaredClass = path; //类标记 var pathAndName = path.split('.'); //分拆包名和类名 var className = pathAndName.pop(); //获取类名 var ns = createNS(pathAndName)||window; //根据传入类路径,创建命名空间 //声明类的实现 var clz =function(){ //如果存在父类,首先调用父类的构造器方法。 if(clz.superclass&&clz.superclass.constructor) clz.superclass.constructor.apply(this,arguments); //如果config参数中设置了声明类的构造器constructor方法,调用声明类的构造器方法。 if(config.constructor) config.constructor.apply(this,arguments); }; if(parent){ if(typeof parent=='function'){ extend(clz,parent); }else if(parent.constructor==Array){ //如果参数设置了多个父类,使用第一个作为声明类的父类,其他使用掺元继承方式实现。 extend(clz,parent[0]); for(var i=1;i<parent.length;i++) mix(parent[i].prototype,clz.prototype); } } //调用父类相关方法 clz.prototype.inherited = function(args){ var caller = arguments.callee.caller; var methodName = caller.nom; return this.superclass[methodName].call(this,args); }; //isInstanceOf方法 clz.prototype.isInstanceOf = function(c){ return (this instanceof c); } //将config定义的属性赋予声明类 for(var p in config){ clz.prototype[p] = config[p]; //如果属性为函数,为函数属性增加'nom'属性,值为函数名的字符串 if(typeof config[p] =='function') clz.prototype[p].nom = p.toString(); } //增加declaredClass标记属性 clz.prototype.declaredClass = declaredClass; //将声明类加入到命名空间 ns[className] = clz; }
测试代码:
//---------------测试--------------------- //定义cn.hou.GrandParent类 declareClass("cn.hou.GrandParent",null,{ sex:'male', familyInfo:{money:1}, //静态属性 constructor:function(param){ this.sex = param.sex; }, getSex:function(){ return this.sex; } }); var gp = new cn.hou.GrandParent({sex:'man'}); console.log(gp.getSex()); //man gp.familyInfo.money++; var gp2 = new cn.hou.GrandParent({sex:'woman'}); console.log(gp2.getSex()); //woman console.log(gp2.familyInfo.money); //2 //掺元测试类 function TestMix(){} TestMix.prototype.test = function(){console.log('TestMix.test');}; //定义cn.hou.Parent子类,继承cn.hou.GrandParent 和 TextMix declareClass("cn.hou.Parent",[cn.hou.GrandParent,TestMix],{ name:null, age:0, constructor:function(param){ this.name = param.name; this.age = param.age; }, getName:function(){ return this.name; }, getAge:function(){ return this.age; }, getSex:function(){ //var sex = this.superclass.getSex.apply(this);//在没有以上inherited方法前,调用父类同名函数 的一种方法。 var sex = this.inherited(arguments); //通过 declareClass 声明函数定义的对象,可以使用inherited方法调用父类同名函数。 return 'parent getSex() called:'+sex; } }); var p = new cn.hou.Parent({name:'test',age:22,sex:'男'}); console.log(p.declaredClass); //cn.hou.Parent console.log(p.getName()); //test console.log(p.getSex()); //parent getSex() called:男 console.log(p instanceof cn.hou.Parent) //true console.log(p.isInstanceOf(cn.hou.Parent))//true console.log(p instanceof TestMix) //false p.test(); //TestMix.test