您好,欢迎访问一九零五行业门户网

javascript面向对象包装类Class封装类库剖析_javascript技巧

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.ageconsole.log('tooyoung!drinkmilkinstead!'):
console.log('whiskeyorbeer?');
}
});
//暴露给命名空间
mylib.person=person;
})();
varjohn=newmylib.person('john',16);
john.sayhello();//loghellofromjohn!
john.drinkalcohol();//logtooyoung!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();//loghellofromsylvester!idreamofeatingtweety!
sylvester.wakeup();//logwakeup!
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//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;indexinherits(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/
其它类似信息

推荐信息