javascript面向对象包装类Class封装类库剖析(3)


//可以传递一个字面量去继承
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=https://www.jb51.net/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
*Followoursoftware:)
*MITLicense
*/
!function(context,f){
//fnTest用来验证是否可能通过正则找出调用super父类方法的方法
varfnTest=https://www.jb51.net/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的环境,就平移退化到

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wddgyf.html