javascript是个入门门槛很低的语言,甚至一个从来没有接触过javascript的技术人员,几小时内就可以写出一个简单有用的程序代码。
但是如果因此你就下结论:javascript是门简单的语言。那你就大错特错了。想写出高性能的代码,同样需要具备一个高级程序员的基本素养。
一个java或者c++程序员,不一定能写出高性能的javascript代码,但更容易写出高性能的javascript代码。
javascript的简单是基于它“胸襟广阔”的包容性。它声明时,不需要指定类型,甚至可以任意的转换类型。它面向对象,却没有类(Class)的限制。它是一门崇尚自由又非常严谨的语言,如果你是一个自由主义者,那么,拥抱javascript吧!
面向对象编程(OOP)是一种流行的编程方法。但javascript的OOP,较之JAVA、c++有很大的同,主要体现它的继承方式不同。javascript是基于原型PROTOTYPE继承的。所有对象都是基于原型链,最终追述到Object对象。
这里不想讨论过多的关于javascript的继承方式和其它语言的继承方式的不同之处。主要讨论如何封装javascript的Class,以便更好的管理和维护基础代码,减少重复代码,以及更好的模块化编程。
下面是几个github上找到的比较好的Class封装类库:
一、MY-CLASS
项目地址:https://github.com/jiem/my-class
先看基本用法:
a、新建一个类
(function(){ //新建类 varPerson=my.Class({ //添加静态方法 STATIC:{ AGE_OF_MAJORITY:18 }, //构造函数 constructor:function(name,age){ this.name=name; this.age=age; }, //实例方法 sayHello:function(){ console.log('Hellofrom'+this.name+'!'); }, //实例方法 drinkAlcohol:function(){ this.age<Person.AGE_OF_MAJORITY? console.log('Tooyoung!Drinkmilkinstead!'): console.log('Whiskeyorbeer?'); } }); //暴露给命名空间 myLib.Person=Person; })(); varjohn=newmyLib.Person('John',16); john.sayHello();//log"HellofromJohn!" john.drinkAlcohol();//log"Tooyoung!Drinkmilkinstead!"
b、继承一个类
(function(){ //Dreamer继承Person varDreamer=my.Class(Person,{ //构造方法 constructor:function(name,age,dream){ Dreamer.Super.call(this,name,age); this.dream=dream; }, //实例方法 sayHello:function(){ superSayHello.call(this); console.log('Idreamof'+this.dream+'!'); }, //实例方法 wakeUp:function(){ console.log('Wakeup!'); } }); //Super访问父类 varsuperSayHello=Dreamer.Super.prototype.sayHello; //暴露给全局命名空间 myLib.Dreamer=Dreamer; })(); varsylvester=newmyLib.Dreamer('Sylvester',30,'eatingTweety'); sylvester.sayHello();//log"HellofromSylvester!IdreamofeatingTweety!" sylvester.wakeUp();//log"Wakeup!"
c、给类添加新方法
//给myLib.Dreamer添加新方法 my.extendClass(myLib.Dreamer,{ //添加静态方法 STATIC:{ s_dongSomeThing:function(){ console.log("dosomething!"); } }, //添加实例方法 touchTheSky:function(){ console.log('Touchingthesky'); }, //添加实例方法 reachTheStars:function(){ console.log('Sheissopretty!'); } });
d、实现一个类的方法
//声明一个新类 myLib.ImaginaryTraveler=my.Class({ travel:function(){console.log('Travelingonacarpet!');}, crossOceans:function(){console.log('SayinghitoMobyDick!');} }); (function(){ //Dreamer继承Person实现ImaginaryTraveler的方法 varDreamer=my.Class(Person,ImaginaryTraveler,{ //构造方法 constructor:function(name,age,dream){ Dreamer.Super.call(this,name,age); this.dream=dream; } //... }); //暴露给全局命名空间 myLib.Dreamer=Dreamer; })(); varaladdin=newDreamer('Aladdin'); aladdininstanceofPerson;//true aladdininstanceofImaginaryTraveler;//false aladdin.travel(); aladdin.wakeUp(); aladdin.sayHello();
如果怕忘记new操作符
varPerson=my.Class({ //youcannowcalltheconstructorwithorwithoutnew constructor:function(name,city){ if(!(thisinstanceofPerson)) returnnewPerson(name,city); this.name=name; this.city=citye; } });
下面看一下my.class的源代码解析:
my.Class实现思路基本是这样的,如果只有一个参数,那么声明的是一个基础类,这个参数是用来声明新类的方法和属以及构造函数。它不是继承而来,但它可以被继承。
继承的思路,就是如果有两个参数,第一个参数做为父类被继承,第二参数用来声明新类的方法和属性以及构造函数,它同样可以被继承。
如果有三个以上参数那么,除出第一个参数做为继承的父类,最后一个参数用声明新类的方法和属性以及构造函数。中间的参数是用类来扩展新类的方法。当然也可以通过my.extendClass扩展新方法。
同时,类库为commonJS和浏览环境都提供了支持!
/*globalsdefine:true,window:true,module:true*/ (function(){ //Namespaceobject varmy={}; //保证AMD分模块可用 if(typeofdefine!=='undefined') define([],function(){ returnmy; }); elseif(typeofwindow!=='undefined') //保证客户端可用 window.my=my; else //保证后台可用 module.exports=my; //============================================================================ //@methodmy.Class //@paramsbody:Object //@paramsSuperClass:function,ImplementClasses:function...,body:Object //@returnfunction my.Class=function(){ varlen=arguments.length; varbody=arguments[len-1];//最后一个参数是指定本身的方法 varSuperClass=len>1?arguments[0]:null;//第一个参数是指继承的方法,实例和静态部分均继承 varhasImplementClasses=len>2;//如果有第三个参数,那么第二个就是implementClass,这里其实只继承实例对象 varClass,SuperClassEmpty; //保证构造方法 if(body.constructor===Object){ Class=function(){}; }else{ Class=body.constructor; //保证后面不覆盖constructor deletebody.constructor; } //处理superClass部分 if(SuperClass){ //中间件实现实例属性的继承 SuperClassEmpty=function(){}; SuperClassEmpty.prototype=SuperClass.prototype; Class.prototype=newSuperClassEmpty();//原型继承,解除引用 Class.prototype.constructor=Class;//保证constructor Class.Super=SuperClass;//父对象访问接口 //静态方法继承,重载superClass方法 extend(Class,SuperClass,false); } //处理ImplementClass部分,其实只继承实例属性部分,除SuperClass#arguments[0]#和body#arguments[length-1]# if(hasImplementClasses) for(vari=1;i<len-1;i++) //implement是继承的实例属性部分,重载父对象implementClass方法 extend(Class.prototype,arguments[i].prototype,false); //处理本身声明body部分,静态要STATIC指定,实例部分要删除STATIC部分 extendClass(Class,body); returnClass; }; //============================================================================ //@methodmy.extendClass //@paramsClass:function,extension:Object,?override:boolean=true varextendClass=my.extendClass=function(Class,extension,override){ //静态部分继承静态部分 if(extension.STATIC){ extend(Class,extension.STATIC,override); //保证实例部分不继承静态方法 deleteextension.STATIC; } //实例属性继继承实例部 extend(Class.prototype,extension,override); }; //============================================================================ varextend=function(obj,extension,override){ varprop; //其实这里的flase是表明,覆盖父对象的方法 if(override===false){ for(propinextension) if(!(propinobj)) obj[prop]=extension[prop]; }else{ //这里其实不覆盖父对象的方法,包括toString for(propinextension) obj[prop]=extension[prop]; if(extension.toString!==Object.prototype.toString) obj.toString=extension.toString; } }; })();
二、KLASS
项目地址:https://github.com/ded/klass
先看使用方法:
a、新建一个类
//声明一个类 varPerson=klass(function(name){ this.name=name }) .statics({//静态方法 head:':)', feet:'_|_' }) .methods({//实例方法 walk:function(){} })
b、继承一个类
//SuperHuman继承Person varSuperHuman=Person.extend(function(name){ //自动调用父类的构造方法 }) .methods({ walk:function(){ //显式声明调用父类的walk方法 this.supr() this.fly() }, fly:function(){} }) newSuperHuman('Zelda').walk()
c、字面量方式声明一个类
varFoo=klass({ foo:0, initialize:function(){ this.foo=1 }, getFoo:function(){ returnthis.foo }, setFoo:function(x){ this.foo=x returnthis.getFoo() } })
d、实现一个类的方法
因为有时候你可能希望覆写或者混合一个实例方法,可以这样:
//可以传递一个字面量去继承 varAlien=SuperHuman.extend({ beam:function(){ this.supr() //beamintospace } }) varSpazoid=newAlien('Zoopo') if(beamIsDown){ //覆写beam方法 Spazoid.implement({ beam:function(){ this.supr() //fallbacktojets this.jets() } }) }
下面看一下klass源代码解析:
klass的基本设计思路很明确,极力的模仿其它语言的继承方式。比如:子类构造方法调用父类的构造方法,还可以显式的声明调用父类的方法。
这种判断都是基于正则匹配:fnTest=/xyz/.test(function(){xyz;})?/\bsupr\b/:/.*/;关键字"super"
如果显示的声明了要调用父类的方法,那么声明方法的时候,就包装成一个内部调用父类方法且返回相同值的函数,给当前类的方法。
另一方面,构造方法,也是比较灵活的。如果显示的声明了initialize,那么这就是构造方法。否则如果参数是个function那么它就做为构造方法,否则就用父类的构造方法。
通过statics方式添加静态方法,通过实例的implements和静态方法methods添加实例方法。
通过父类的extend实现继承。
同时,类库为commonJS和浏览环境都提供了支持!
/** *Klass.js-copyright@dedfat *version1.0 *https://github.com/ded/klass *Followoursoftwarehttp://twitter.com/dedfat:) *MITLicense */ !function(context,f){ //fnTest用来验证是否可能通过正则找出调用super父类方法的方法 varfnTest=/xyz/.test(function(){xyz;})?/\bsupr\b/:/.*/, noop=function(){}, proto='prototype', isFn=function(o){ returntypeofo===f; }; //基础类 functionklass(o){ returnextend.call(typeofo==f?o:noop,o,1); } //包装成一个借用super同名方法的函数 functionwrap(k,fn,supr){ returnfunction(){ //缓存原this.super vartmp=this.supr; //暂把this.super改造成借用super的同名方法above //供o里显式的声明(fnTest.text(fn)==true)要借用super的同名方法使用 this.supr=supr[proto][k]; //借用执行并保存返回值 varret=fn.apply(this,arguments); //恢复原this.super this.supr=tmp; //返回返回值,保证wrap后的返回值跟原来一致 returnret; }; } //如果o和super有同名方法,且o显式声明借用super的同名方法,就wrap成一个待执行函数供使用 //如果没有显式的声明借用super的同名方法,或者是o独有的方法,或者不是方法就直接用 functionprocess(what,o,supr){ for(varkino){ //如果是非继承方法,按方法注释规则执行,最终都放进what if(o.hasOwnProperty(k)){ what[k]=typeofo[k]==f &&typeofsupr[proto][k]==f &&fnTest.test(o[k]) ?wrap(k,o[k],supr):o[k]; } } } //继承方法的实现,fromSub是用来控制是否继承而来,上面的klass里面fromSub是1,表明非继承而来,构造函数不借用super执行 functionextend(o,fromSub){ //noop做为媒介类实现原型继承的解除引用 noop[proto]=this[proto]; varsupr=this, prototype=newnoop(),//创建实例对象供原型继承使用,解除引用 isFunction=typeofo==f, _constructor=isFunction?o:this,//如果o是一个构造方法就用,否则由this来决定构造函数 _methods=isFunction?{}:o,//如果o是一个{...}应该用methods放到fn原型里,如果里面有initialize就是构造函数,如果o是函数就由上面_constructor决定o是构造函数 fn=function(){//因为kclass借助了kclass,所以最终实际上返回的就是fn,fn其实就新类的构造函数 //1如果o是{...}就会被methods直接过滤并添加到fn的原型里,如果o里面有initialize,那么fn的原型里就有initialize,那么它就是构造方法 //2如果o是function,methods什么也添加不到fn的原型里,但是_constructor会接受o当构造函数 //3如果o是{....},同时里面也没有initialize,那么就是this当构造函数,如果在klass里由call决定,显然构造函数是noop,如果在非基础类里,构造函数就是父类的构造函数 //由于o不是函数不会自动调用父类的构造函数,只是把父类的构造函数当做当前类的构造函数----这都是由于this的指向决定的 console.log(this); if(this.initialize){ this.initialize.apply(this,arguments); }else{ //调用父类构造方法 //如上面3,o不是函数,不会调用父类的构造方法 //基础类无父类,不会调用父类构造方法 fromSub||isFn(o)&&supr.apply(this,arguments); //调用本类构造方法 //参考上面2,3要么是noop要么是o console.log(_constructor==noop); _constructor.apply(this,arguments); } }; //构造原型方法的接口 fn.methods=function(o){ process(prototype,o,supr); fn[proto]=prototype; returnthis; }; //执行实现新类原型,保证新类的constructor fn.methods.call(fn,_methods).prototype.constructor=fn; //保证新类可以被继承 fn.extend=arguments.callee; //添加实例方法或者静态方法,statics:静态方法,implement实例方法 fn[proto].implement=fn.statics=function(o,optFn){ //保证o是一个object对象,如果o是一个字符串,那么就是添一个方法的情况,如果o是一个object对象说明是批量添加的 //因为要从o里面拷贝 o=typeofo=='string'?(function(){ varobj={}; obj[o]=optFn; returnobj; }()):o; //添加实例方法或者静态方法,statics:静态方法,implement实例方法 process(this,o,supr); returnthis; }; returnfn; } //后台用,nodejs if(typeofmodule!=='undefined'&&module.exports){ module.exports=klass; }else{ varold=context.klass; //防冲突 klass.noConflict=function(){ context.klass=old; returnthis; }; //前台浏览器用 //window.kclass=kclass; context.klass=klass; } }(this,'function');
三、还有一种简单实现
实现思路很简单,就是利用ECMAScript5原型式继承Object.create方法,封装成一个方法,如果不支持ECMAScript5的环境,就平移退化到
functionF(){}; F.prototype=superCtor.prototype; ctor.prototype=newF(); ctor.prototype.constructor=ctor;
同样的,除最后一个参数是当前类的方法声明,其它参数均做为继承父类,需要循环继承,但当这里处理的相对比较简单,没涉及到覆盖。你可以自己动手添加。
varClass=(function(){ /** *Inheritsfunction.(node.js) * *@paramctorsubclass'sconstructor. *@paramsuperctorsuperclass'sconstructor. */ varinherits=function(ctor,superCtor){ //显式的指定父类 ctor.super_=superCtor; //ECMAScript5原型式继承并解除引用 if(Object.create){ ctor.prototype=Object.create(superCtor.prototype,{ constructor:{ value:ctor, enumerable:false, writable:true, configurable:true } }); }else{ //无Object.create方法的平稳退化 functionF(){}; F.prototype=superCtor.prototype; ctor.prototype=newF(); ctor.prototype.constructor=ctor; } }; /** *Classfunction. */ returnfunction(){ //最后一个参数是新类方法、属性和构造函数声明 varsubClazz=arguments[arguments.length-1]||function(){}; //initialize是构造函数,否构造函数就是一个空函数 varfn=subClazz.initialize==null?function(){}:subClazz.initialize; //继承除最一个参数以的类,多继承,也可以用作扩展方法 for(varindex=0;index<arguments.length-1;index++){ inherits(fn,arguments[index]); } //实现新类的方法 for(varpropinsubClazz){ if(prop=="initialize"){ continue; } fn.prototype[prop]=subClazz[prop]; } returnfn; } })();
看下面实例:
/** *ThedefinitionofCatClass. */ varCat=Class({ /** *Constructor. * *@paramnameCat'sname */ initialize:function(name){ this.name=name; }, /** *Eatfunction. */ eat:function(){ alert(this.name+"iseatingfish."); } }); /** *ThedefinitionofBlackCatClass. */ varBlackCat=Class(Cat,{ /** *Constructor. * *@paramnameCat'sname. *@paramageCat'sage. */ initialize:function(name,age){ //calltheconstructorofsuperclass. BlackCat.super_.call(this,name); this.age=age; }, /** *Eatfunction. */ eat:function(){ alert(this.name+"("+this.age+")iseatingdog."); } }); /** *ThedefinitionofBlackFatCatClass. */ varBlackFatCat=Class(BlackCat,{ /** *Constructor. * *@paramnameCat'sname. *@paramageCat'sage. *@paramweightCat'sweight. */ initialize:function(name,age,weight){ //calltheconstructorofsuperclass. BlackFatCat.super_.call(this,name,age); this.weight=weight; }, /** *Eatfunction. */ eat:function(){ alert(this.name+"("+this.age+")iseatingdog.Myweight:"+this.weight); } }); /** *ThedefinitionofDogClass. */ varDog=Class({}); varcat=newBlackFatCat("John",24,"100kg"); cat.eat(); //true alert(catinstanceofCat); //true alert(catinstanceofBlackCat); //true alert(catinstanceofBlackFatCat); //true alert(cat.constructor===BlackFatCat); //false alert(catinstanceofDog);
四、mootools类库的Class
源码解析可以看这里:http://www.cnblogs.com/hmking/archive/2011/09/30/2196504.html
看具体用法:
a、新建一个类
varCat=newClass({ initialize:function(name){ this.name=name; } }); varmyCat=newCat('Micia'); alert(myCat.name);//alerts'Micia' varCow=newClass({ initialize:function(){ alert('moooo'); } });
b、继承的实现
varAnimal=newClass({ initialize:function(age){ this.age=age; } }); varCat=newClass({ Extends:Animal, initialize:function(name,age){ this.parent(age);//callsinitalizemethodofAnimalclass this.name=name; } }); varmyCat=newCat('Micia',20); alert(myCat.name);//alerts'Micia'. alert(myCat.age);//alerts20.
c、扩充类的实现
varAnimal=newClass({ initialize:function(age){ this.age=age; } }); varCat=newClass({ Implements:Animal, setName:function(name){ this.name=name } }); varmyAnimal=newCat(20); myAnimal.setName('Micia'); alert(myAnimal.name);//alerts'Micia'.
五、悟透javascript:语法甘露
先看用法实例
a、创建类
//创建类Person varPerson=Class(object,{ Create:function(name,age){ this.name=name; this.age=age; }, SayHello:function(){ alert("Hello,I'm"+this.name+","+this.age+"yearsold."); } }); varBillGates=New(Person,["BillGates",53]); BillGates.SayHello();
b、继承类
//Employee继承Person varEmployee=Class(Person,{ Create:function(name,age,salary){ Person.Create.call(this,name,age); //调用基类的构造函数 this.salary=salary; }, ShowMeTheMoney:function(){ alert(this.name+"$"+this.salary); } }); varSteveJobs=New(Employee,["SteveJobs",53,1234]); SteveJobs.SayHello(); SteveJobs.ShowMeTheMoney();
下面是源码分析:显然,多了一个New方法,创建类和新建类的实例都被巧妙的封装了。形成了一个有意义的整体!还有一点不同的地方,所有的类都基于字面量,而不是基于函数。代码很简短,但其中原理却很丰富也很巧妙,可以细细品味一番!
//创建类的函数,用于声明类及继承关系 functionClass(aBaseClass,aClassDefine){ //创建类的临时函数壳 functionclass_(){ this.Type=aBaseClass; //我们给每一个类约定一个Type属性,引用其继承的类 for(varmemberinaClassDefine) this[member]=aClassDefine[member]; //复制类的全部定义到当前创建的类 }; class_.prototype=aBaseClass; returnnewclass_(); }; //创建对象的函数,用于任意类的对象创建 functionNew(aClass,aParams){ //创建对象的临时函数壳 functionnew_(){ this.Type=aClass; //我们也给每一个对象约定一个Type属性,据此可以访问到对象所属的类 if(aClass.Create) aClass.Create.apply(this,aParams); //我们约定所有类的构造函数都叫Create,这和DELPHI比较相似 }; new_.prototype=aClass; returnnewnew_(); };
由于写的比较笼统,可能有很多地方没有解析到,也可能有不准确的地方,还望指正。
看完上面几种解析,相信息自己也可以写出一个自己的封装类库出来,至于,怎么实现看个人喜好了。但基本的思都是一样的基于原型的继承方式和循环拷贝新方法。
原文来自:穆乙 http://www.cnblogs.com/pigtail/
javascript面向对象包装类Class封装类库剖析
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@