深入理解JavaScript:代码复用模式(推荐篇)
2012-12-02 09:04
549 查看
介绍
本文介绍的四种代码复用模式都是最佳实践,推荐大家在编程的过程中使用。模式1:原型继承
原型继承是让父对象作为子对象的原型,从而达到继承的目的:functionobject(o){ functionF(){ } F.prototype=o; returnnewF(); } //要继承的父对象 varparent={ name:"Papa" }; //新对象 varchild=object(parent); //测试 console.log(child.name);//"Papa" //父构造函数 functionPerson(){ //an"own"property this.name="Adam"; } //给原型添加新属性 Person.prototype.getName=function(){ returnthis.name; }; //创建新person varpapa=newPerson(); //继承 varkid=object(papa); console.log(kid.getName());//"Adam" //父构造函数 functionPerson(){ //an"own"property this.name="Adam"; } //给原型添加新属性 Person.prototype.getName=function(){ returnthis.name; }; //继承 varkid=object(Person.prototype); console.log(typeofkid.getName);//"function",因为是在原型里定义的 console.log(typeofkid.name);//"undefined",因为只继承了原型
同时,ECMAScript5也提供了类似的一个方法叫做Object.create用于继承对象,用法如下:
/*使用新版的ECMAScript5提供的功能*/ varchild=Object.create(parent); varchild=Object.create(parent,{ age:{value:2}//ECMA5descriptor }); console.log(child.hasOwnProperty("age"));//true
而且,也可以更细粒度地在第二个参数上定义属性:
//首先,定义一个新对象man varman=Object.create(null); //接着,创建包含属性的配置设置 //属性设置为可写,可枚举,可配置 varconfig={ writable:true, enumerable:true, configurable:true }; //通常使用Object.defineProperty()来添加新属性(ECMAScript5支持) //现在,为了方便,我们自定义一个封装函数 vardefineProp=function(obj,key,value){ config.value=value; Object.defineProperty(obj,key,config); } defineProp(man,'car','Delorean'); defineProp(man,'dob','1981'); defineProp(man,'beard',false);
所以,继承就这么可以做了:
vardriver=Object.create(man); defineProp(driver,'topSpeed','100mph'); driver.topSpeed//100mph
但是有个地方需要注意,就是Object.create(null)创建的对象的原型为undefined,也就是没有toString和valueOf方法,所以alert(man);的时候会出错,但alert(man.car);是没问题的。
模式2:复制所有属性进行继承
这种方式的继承就是将父对象里所有的属性都复制到子对象上,一般子对象可以使用父对象的数据。先来看一个浅拷贝的例子:
/*浅拷贝*/ functionextend(parent,child){ vari; child=child||{}; for(iinparent){ if(parent.hasOwnProperty(i)){ child[i]=parent[i]; } } returnchild; } vardad={name:"Adam"}; varkid=extend(dad); console.log(kid.name);//"Adam" vardad={ counts:[1,2,3], reads:{paper:true} }; varkid=extend(dad); kid.counts.push(4); console.log(dad.counts.toString());//"1,2,3,4" console.log(dad.reads===kid.reads);//true
代码的最后一行,你可以发现dad和kid的reads是一样的,也就是他们使用的是同一个引用,这也就是浅拷贝带来的问题。
我们再来看一下深拷贝:
/*深拷贝*/ functionextendDeep(parent,child){ vari, toStr=Object.prototype.toString, astr="[objectArray]"; child=child||{}; for(iinparent){ if(parent.hasOwnProperty(i)){ if(typeofparent[i]==='object'){ child[i]=(toStr.call(parent[i])===astr)?[]:{}; extendDeep(parent[i],child[i]); }else{ child[i]=parent[i]; } } } returnchild; } vardad={ counts:[1,2,3], reads:{paper:true} }; varkid=extendDeep(dad); kid.counts.push(4); console.log(kid.counts.toString());//"1,2,3,4" console.log(dad.counts.toString());//"1,2,3" console.log(dad.reads===kid.reads);//false kid.reads.paper=false;
深拷贝以后,两个值就不相等了,bingo!
模式3:混合(mix-in)
混入就是将一个对象的一个或多个(或全部)属性(或方法)复制到另外一个对象,我们举一个例子:functionmix(){ vararg,prop,child={}; for(arg=0;arg<arguments.length;arg+=1){ for(propinarguments[arg]){ if(arguments[arg].hasOwnProperty(prop)){ child[prop]=arguments[arg][prop]; } } } returnchild; } varcake=mix( {eggs:2,large:true}, {butter:1,salted:true}, {flour:'3cups'}, {sugar:'sure!'} ); console.dir(cake);
mix函数将所传入的所有参数的子属性都复制到child对象里,以便产生一个新对象。
那如何我们只想混入部分属性呢?该个如何做?其实我们可以使用多余的参数来定义需要混入的属性,例如mix(child,parent,method1,method2)这样就可以只将parent里的method1和method2混入到child里。上代码:
//Car varCar=function(settings){ this.model=settings.model||'nomodelprovided'; this.colour=settings.colour||'nocolourprovided'; }; //Mixin varMixin=function(){}; Mixin.prototype={ driveForward:function(){ console.log('driveforward'); }, driveBackward:function(){ console.log('drivebackward'); } }; //定义的2个参数分别是被混入的对象(reciving)和从哪里混入的对象(giving) functionaugment(receivingObj,givingObj){ //如果提供了指定的方法名称的话,也就是参数多余3个 if(arguments[2]){ for(vari=2,len=arguments.length;i<len;i++){ receivingObj.prototype[arguments[i]]=givingObj.prototype[arguments[i]]; } } //如果不指定第3个参数,或者更多参数,就混入所有的方法 else{ for(varmethodNameingivingObj.prototype){ //检查receiving对象内部不包含要混入的名字,如何包含就不混入了 if(!receivingObj.prototype[methodName]){ receivingObj.prototype[methodName]=givingObj.prototype[methodName]; } } } } //给Car混入属性,但是值混入'driveForward'和'driveBackward'*/ augment(Car,Mixin,'driveForward','driveBackward'); //创建新对象Car varvehicle=newCar({model:'FordEscort',colour:'blue'}); //测试是否成功得到混入的方法 vehicle.driveForward(); vehicle.driveBackward();
该方法使用起来就比较灵活了。
模式4:借用方法
一个对象借用另外一个对象的一个或两个方法,而这两个对象之间不会有什么直接联系。不用多解释,直接用代码解释吧:varone={ name:'object', say:function(greet){ returngreet+','+this.name; } }; //测试 console.log(one.say('hi'));//"hi,object" vartwo={ name:'anotherobject' }; console.log(one.say.apply(two,['hello']));//"hello,anotherobject" //将say赋值给一个变量,this将指向到全局变量 varsay=one.say; console.log(say('hoho'));//"hoho,undefined" //传入一个回调函数callback varyetanother={ name:'Yetanotherobject', method:function(callback){ returncallback('Hola'); } }; console.log(yetanother.method(one.say));//"Holla,undefined" functionbind(o,m){ returnfunction(){ returnm.apply(o,[].slice.call(arguments)); }; } vartwosay=bind(two,one.say); console.log(twosay('yo'));//"yo,anotherobject" //ECMAScript5给Function.prototype添加了一个bind()方法,以便很容易使用apply()和call()。 if(typeofFunction.prototype.bind==='undefined'){ Function.prototype.bind=function(thisArg){ varfn=this, slice=Array.prototype.slice, args=slice.call(arguments,1); returnfunction(){ returnfn.apply(thisArg,args.concat(slice.call(arguments))); }; }; } vartwosay2=one.say.bind(two); console.log(twosay2('Bonjour'));//"Bonjour,anotherobject" vartwosay3=one.say.bind(two,'Enchanté'); console.log(twosay3());//"Enchanté,anotherobject"
引用,查看原文
相关文章推荐
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)详解
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)详解
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 复制所有属性进行继承 转自 博客园 汤姆大叔 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(46):代码复用模式(推荐篇)
- 深入理解JavaScript系列(45):代码复用模式(避免篇)
- 深入理解JavaScript系列(45):代码复用模式(避免篇)
- 深入理解JavaScript系列(45):代码复用模式(避免篇)
- 深入理解JavaScript系列(45):代码复用模式(避免篇)详解
- 深入理解JavaScript系列(45):代码复用模式(避免篇)
- 深入理解JavaScript系列(45):代码复用模式(避免篇)详解