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