javascript面向对象学习笔记(三)——继承
原型链
原型链是实现继承的主要方法,利用原型让一个引用类型继承另一个引用类型的属性和方法。(然一个原型对象等于另一个类型的实例)
实现原型链的一种基本模式:
function SuperType(){
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();
alert(instance.getSuperValue); //true
subType继承了SuperType,而继承是通过创建SuperType的实例,并将该实例赋给SubType.property实现。实现的本质是重写原型对象,代之以一个新类型的实例。
在上面的代码中,没有使用SubType默认提供的原型,而是给他换了一个新原型(SuperType的实例),使得新原型不仅具有一个作为SuperType的实例所拥有的全部属性和方法,其内部还有一个指针指向SuperType的原型。
instance.constructor现在指向SuperType,因为SubType.prototype=new SuperType();var instance=new SubType();重写原型导致SubType对象和instance失去了默认的constructor属性,因此搜索到的constructor属性是SuperType()的,因此指向SuperType.
**确定原型和实例的关系:**1、使用instanceof操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回true。
alert(instance instanceof Object);//true
alert(instance instanceof SuperType);//true
alert(instance instanceof SubType);//true
instance 是Object、SuperType、SubType中任何一个类型的实例。
2、isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。因此isPropertyOf()方法返回true。
alert(Object.protopyte.isPrototypeOf(instance));//true
alert(SuperType.prototype.isPrototypeOf(instance));//true
alert(SubType.prototype.isPrototypeOf(instance));//true
给原型添加方法的代码一定要放在替换原型的语句之后。
function SuperType(){
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;
};
//重写超类型中的方法
SubType.prototype.getSuperValue=function(){
return false;
};//此时该方法将屏蔽原来原型链中已存在的getSuperValue()方法;要写在SuperType的实例替换原型之后,即SubType.prototype=new SuperType()之后
var instance=new SubType();
alert(instance.getSuperValue); //false
通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链。
function SuperType(){
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;
},
someOtherMethod:function(){
return false;
}
};//使用字面量添加新方法,会导致上一行代码无效,此时原型包含的是Object实例而非SuperType的实例.
var instance=new SubType();
alert(instance.getSuperValue());//error
原型链的问题:主要来自包含引用类型值得原型。在通过原型实现继承时,原型实际上会变成另一个类型的的实例,于是原先的实例属性顺理成章的变成了现在原型的属性。
function SuperType(){
this.colors=["red","blue","green"];
}
function SubType(){}
//继承了SuperType
SubType.prototype=new SuperType();
var instance1=new SuperType();
instance1.colors.push("black");
alert(instance1.colors);//red,blue,green,black
var instance2=new SuperType();
alert(instance2.colors);//red,blue,green,black//SubType的所有实例共享这一个colors属性
借用构造函数
基本思想:在子类型构造函数的内部调用超类型构造函数。
function SuperType(){
this.colors=["red","blue","green"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);//借调超类型的构造函数
}
var instance1=new SubType();
instance1.colors.push("black");
alert(instance.colors);//red,blue,green,black
var instance2=new SubType();
alert(instance2.colors);//red,blue,green
与原型链对比的优势:可以在子类型构造函数中向超类型构造函数传递参数.
function SuperType(name){
this.name=name;
}
function SubType(){
SuperType.call(this,"Chiaki");//为SubType的实例设置name属性
this.age=21;
}
var instance=new SubType();
alert(instance.name);//Chiaki
alert(instance.age);//21
借用构造函数的问题:方法都在构造函数中定义,缺少函数的复用;且在超类型的原型中定义的方法,对子类型不可见
组合继承
将原型链和借用构造函数的技术组合到一起,使用原型链实现原型属性和方法的基础,通过借用构造函数来实现对实例属性的基础。
function SuperType(){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
alert(this.name);
};
function SubType(name,age){
SuperType.call(this.name);//调用SuperType构造函数时传入name参数
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.sayAge=function(){
alert(this.age);
}
var instance1=new SubType("Chiaki",21);
instance1.colors.push("black");
alert(instance1.color);//red,blue,green,black
instance1.sayName();//Chiaki
instance1.sayAge();//21
var instance2=new SubType("Wu",20);
alert(instance2.color);//red,blue,green
instance2.sayName();//Wu
instance2.sayAge();//20
原型式继承
借助原型可以基于已有对象创建新对象,同时还不必因此创建自定类型。
function object(o){
funtion F(){
F.prototype=o;
return new F();
}
}
在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例.。从本质上讲,object()对传入其中的对象执行了一次浅复制.
var person={
name:"Chiaki",
friends:["lola","cherry","van"]
};
var anotherPerson=object(person);
anotherPerson.name="Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson=object(person);
yetAnotherPerson.name="linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends);
原型模式继承要求必须要有一个对象作为另一个对象的基础,把它传递给object()函数,然后再根据具体需求对得到的对象加以修改即可。
Object.create()
方法规范化了原型式继承。该方法接收了两个参数:一个用作新对象原型的对象和一个作为新对象而外属性的对象。适用于IE9+,Firefox4+,Safari 5+,Opera 12+,Chrome
var anotherPerson=Object.create(person,{
name:{
value:"Chiaki"
}
});
寄生式继承
创建一个仅用于封装仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真的是它做了所有工作一样返回对象。以下代码示范了寄生式继承模式:
function createAnother(original){
var clone=object(original);//通过调用函数创建一个新对象
clone,sayHi=function(){//以某种方式来增强这个对象
alert("hi");
};
return clone;//返回这个对象
}
可以像下面那样使用createAnother()函数。
var person={
name:"Chiaki",
friends:["lola","cherry"]
};
var anothePerson=createPerson(person);
anotherPerson.sayHi();//hi
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。
寄生组合式继承
组合继承的问题:无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
alert(this.name);
}
function SubType(name,age){
SuperType.call(this.name);//第二次调用SuperType()
this.age=age;
}
SubType.prototype=new SuperType();//第二次调用SuperType()
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
alert(this.age);
}
寄生式组合继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。基本模式如下:
function inheritPrototype(subType,superType){
var prototype=object(superType.prototype);//创建对象
prototype.constructor=subType;//增强对象//弥补因重写原型二失去的默认的constructor属性
subType.prototype=prototype; //指定对象
}
inheritPrototype()函数接收两个参数:子类型构造函数和超类型构造函数。
function SuperType(name){
this.name=name;
this.colors=["red","blue","green"];
}
SuperType.prototype.sayName=function(){
alert(this.name);
}
function SubType(name,age){
SuperType.call(this.name);
this.age=age;
}
inheritPrototype(subType,SuperType);
SubType.prototype.sayAge=function(){
alert(this.age);
}
使用inheritPrototype()只调用了一次SuperType构造函数,提高了效率
【参考自《javascript高级程序设计(第三版)》】