Javascript OOP之面向对象
面向对象程序设计(Object-orientedprogramming,OOP)是一种程序设计范型,同时也是一种程序开发的方法。对象指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。——维基百科
一般面向对象包含:继承,封装,多态,抽象
对象形式的继承
浅拷贝
varPerson={ name:'allin', age:18, address:{ home:'home', office:'office', } sclools:['x','z'], }; varprogramer={ language:'js', }; functionextend(p,c){ varc=c||{}; for(varpropinp){ c[prop]=p[prop]; } } extend(Person,programer); programer.name;//allin programer.address.home;//home programer.address.home='house';//house Person.address.home;//house
从上面的结果看出,浅拷贝的缺陷在于修改了子对象中引用类型的值,会影响到父对象中的值,因为在浅拷贝中对引用类型的拷贝只是拷贝了地址,指向了内存中同一个副本。
深拷贝
functionextendDeeply(p,c){ varc=c||{}; for(varpropinp){ if(typeofp[prop]==="object"){ c[prop]=(p[prop].constructor===Array)?[]:{}; extendDeeply(p[prop],c[prop]); }else{ c[prop]=p[prop]; } } }
利用递归进行深拷贝,这样子对象的修改就不会影响到父对象。
extendDeeply(Person,programer); programer.address.home='allin'; Person.address.home;//home 利用call和apply继承 functionParent(){ this.name="abc"; this.address={home:"home"}; } functionChild(){ Parent.call(this); this.language="js"; } ES5中的Object.create() varp={name:'allin'}; varobj=Object.create(o); obj.name;//allin
Object.create()作为new操作符的替代方案是ES5之后才出来的。我们也可以自己模拟该方法:
//模拟Object.create()方法 functionmyCreate(o){ functionF(){}; F.prototype=o; o=newF(); returno; } varp={name:'allin'}; varobj=myCreate(o); obj.name;//allin
目前,各大浏览器的最新版本(包括IE9)都部署了这个方法。如果遇到老式浏览器,可以用下面的代码自行部署。
if(!Object.create){ Object.create=function(o){ functionF(){} F.prototype=o; returnnewF(); }; }
类的继承
Object.create() functionPerson(name,age){} Person.prototype.headCount=1; Person.prototype.eat=function(){ console.log('eating...'); } functionProgrammer(name,age,title){} Programmer.prototype=Object.create(Person.prototype);//建立继承关系 Programmer.prototype.constructor=Programmer;//修改constructor的指向
调用父类方法
functionPerson(name,age){ this.name=name; this.age=age; } Person.prototype.headCount=1; Person.prototype.eat=function(){ console.log('eating...'); } functionProgrammer(name,age,title){ Person.apply(this,arguments);//调用父类的构造器 } Programmer.prototype=Object.create(Person.prototype); Programmer.prototype.constructor=Programmer; Programmer.prototype.language="js"; Programmer.prototype.work=function(){ console.log('iamworkingcodein'+this.language); Person.prototype.eat.apply(this,arguments);//调用父类上的方法 }
封装
命名空间
js是没有命名空间的,因此可以用对象模拟。
varapp={};//命名空间app //模块1 app.module1={ name:'allin', f:function(){ console.log('hirobot'); } }; app.module1.name;//"allin" app.module1.f();//hirobot
静态成员
functionPerson(name){ varage=100; this.name=name; } //静态成员 Person.walk=function(){ console.log('static'); }; Person.walk();//static
私有与公有
functionPerson(id){ //私有属性与方法 varname='allin'; varwork=function(){ console.log(this.id); }; //公有属性与方法 this.id=id; this.say=function(){ console.log('sayhello'); work.call(this); }; }; varp1=newPerson(123); p1.name;//undefined p1.id;//123 p1.say();//sayhello123
模块化
varmoduleA; moduleA=function(){ varprop=1; functionfunc(){} return{ func:func, prop:prop }; }();//立即执行匿名函数
prop,func不会被泄露到全局作用域。或者另一种写法,使用new
moduleA=newfunction(){ varprop=1; functionfunc(){} this.func=func; this.prop=prop; }
多态
模拟方法重载
arguments属性可以取得函数调用的实参个数,可以利用这一点模拟方法的重载。
functiondemo(a,b){ console.log(demo.length);//得到形参个数 console.log(arguments.length);//得到实参个数 console.log(arguments[0]);//第一个实参4 console.log(arguments[1]);//第二个实参5 } demo(4,5,6); //实现可变长度实参的相加 functionadd(){ vartotal=0; for(vari=arguments.length-1;i>=0;i--){ total+=arguments[i]; } returntotal; } console.log(add(1));//1 console.log(add(1,2,3));//7 //参数不同的情况 functionfontSize(){ varele=document.getElementById('js'); if(arguments.length==0){ returnele.style.fontSize; }else{ ele.style.fontSize=arguments[0]; } } fontSize(18); console.log(fontSize()); //类型不同的情况 functionsetting(){ varele=document.getElementById('js'); if(typeofarguments[0]==="object"){ for(varpinarguments[0]){ ele.style[p]=arguments[0][p]; } }else{ ele.style.fontSize=arguments[0]; ele.style.backgroundColor=arguments[1]; } } setting(18,'red'); setting({fontSize:20,backgroundColor:'green'});
方法重写
functionF(){} varf=newF(); F.prototype.run=function(){ console.log('F'); } f.run();//F f.run=function(){ console.log('fff'); } f.run();//fff
抽象类
在构造器中thrownewError('');抛异常。这样防止这个类被直接调用。
functionDetectorBase(){ thrownewError('Abstractclasscannotbeinvokeddirectly!'); } DetectorBase.prototype.detect=function(){ console.log('Detectionstarting...'); }; DetectorBase.prototype.stop=function(){ console.log('Detectionstopped.'); }; DetectorBase.prototype.init=function(){ thrownewError('Error'); }; //vard=newDetectorBase();//UncaughtError:Abstractclasscannotbeinvokeddirectly! functionLinkDetector(){} LinkDetector.prototype=Object.create(DetectorBase.prototype); LinkDetector.prototype.constructor=LinkDetector; varl=newLinkDetector(); console.log(l);//LinkDetector{}__proto__:LinkDetector l.detect();//Detectionstarting... l.init();//UncaughtError:Error