js中面向对象
类有封装、继承的特点,那么先从这两点开始来看js中如何实现的。
封装
大家都知道如何创建一个对象:obj.name="myobject";
obj.sayName=function(){
return this.name;
};var o =new Object();
o.name=name;
o.sayName=function(){
return this.name;
};
return o;
}这个虽然可以解决以上的问题,但无法识别对象类型。
构造函数模式:
this.name=name;
this.sayName=function(){
return this.name;
};
}var person1=new Person("xiaoqi");
var person2=new Person("xiaosan");
缺点:每创建一个实例都要创建一个方法,内存中会出现重复的相同功能的重复方法,无必要的浪费了内存
解决方法:
this.name=name;
this.sayName=sayName;
}function sayName(){
return this.name;
}var person1=new Person("xiaoqi");
var person2=new Person("xiaosan");
这样person1与person2的sayName属性保存的只是sayName的内存地址,而不用重新创建新的方法。
缺点:
1.定义很多方法,就要定义很多全局函数
2.函数全局作用域名不副实,全局作用域中定义的函数实际上只能被某个对象调用
3.没有丝毫封装性可言
要解决上面的问题引进了原型模式:
Person.prototype.name="myName";
Person.prototype. sayName=function(){
return this.name;
};Person.prototype指向的就是Person的原型对象,实例person1与person2也都有一个指针指向此原型对象。
简单原型:
Person.prototype={
name:"myName",
sayName:function(){
return this.name;
}
};如:
person instanceof Person //true
person instanceof Object //true
person.constructor==Person //false
person.constructor==Object //true
constructor:Person,
name:"myName",
sayName:function(){
return this.name;
}
};
person.constructor==Person //true
person.constructor==Object //false
有人说Person也是一种Object为什么会出现这种结果呢。 这个在后面继承中会再做解释。
原型动态性:
修改原型中的属性,可立即在实例中得到体现,如:
Person.prototype.sayHi=function(){
alert("Hi");
};person.sayHi(); //Hi
但重写了prototype就会不同了:
var person=new Person();
Person.prototype={
constructor:Person,
name:"myName",
sayName:function(){
return this.name;
}};
person.sayName(); //error 访问不到sayName方法
这里person实例重写之后的原型仍然指向原来的指针,原来没有sayName方法,重写后仍然也就没有了。
原型缺点:原型对于方法避免了重复定义方法,节省了内存空间,很是适用。但对于属性就不合适了,尤其是引用类型的属性。请看:
Person.prototype={
constructor:Person,
friends:["s","c"]
};var person1=new Person();
var person2=new Person();
person1.friends.push("v");
person1.friends //"s,c,v"
person2.friends //"s,c,v"
这样就产生了最为常用的模式:组合使用构造函数与原型模式:
this.name=name;
this.job=["s","c"];
}Person.prototype={
constructor:Person,
sayName:function(){
return this.name;
}
};
1.自己独有的属性
2.公用的方法,节省内存
3.支持传递参数
结构图如:
至此,js模仿类的封装已经完结,这个模式是最为常用的一个模式。 这里的Person是构造函数,Person prototype是Person的原型,
person是Person的实例。这里要注意一点,构造函数也是一个实例,是Function的实例,继承自Object,所以Person也是一个对象,它也有自己的属性,如prototype是构造函数时自动生成的,同时也可以自定义属性、方法,如:default属性。
继承
后台继承分为接口继承与实现继承两种,我们在这里只介绍实现继承。实现的基本思路是借用构造函数的prototype指针指向,如果把这个指向另一人构造函数的实例会怎么样呢。如:
this.property=true;
}SuperType.prototype.getSuperValue=function(){
return this.property;
};function SubType(){
this.subproperty=false;
}SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
return this.subproperty;
};调用:
var instance=new SubType();
结构图如:
SubType的原型为SuperType的一个实例即SubType prototype,则SubType prototype指向SuperType的原型即SuperType prototype。如此类推下去就形成一个链式结构,我们称之为原型链。
借用原型链SubType就继承到了SuperType的所有属性与方法。
注意SuperType Prototype本身就是个对象,它也是继承了Object的实例。故:
问题:
1.超类型属性为引用类型时,所有实例都共享此属性
2.无法向超类型的构造函数传参数
解决以上问题借用构造函数:
this.name=name;
this.colors=["s","c"];
}SuperType.prototype.getSuperValue=function(){};
function SubType=function(name){
SuperType.call(this,name);
this.age=29;
}组合继承:
this.name=name;
this.colors=["s","c"];
}SuperType.prototype.getSuperValue=function(){
};
function SubType(name,age){
SuperType.call(this,name); //第二次调用
this.age=age;
}SubType.prototype=new SuperType(); //第一次调用
SubType.prototype.sayAge=function(){
return this.age;
};但SuperType的属性有存在两组:
寄生式组合继承:
this.name=name;
this.colors=["s","c"];
}SuperType.prototype.getSuperValue=function(){
};
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}//
function F(){}
F.prototype=SuperType.prototype;
SubType.prototype=new F();
SubType.prototype.contructor=SubType;
//
SubType.prototype.sayAge=function(){
return this.age;
};至此,js中模仿类的封装与继承就介绍完毕了。