// ECMAScript将原型链作为继承的主要方法,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
// 构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象对象都有一个指向构造函数的指针,
// 而实例都包含一个指向原型对象的内部指针
function SuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
}
function SubType(){
this.subproperty=false;
}
// 继承了SuperType
SubType.prototype=new SuperType();
//又在SubType.prototype中添加了一个方法。
SubType.prototype.getSubValue=function(){
return this.subproperty;
}
var instance=new SubType();
console.log(instance.getSuperValue()); // true
// 以上代码是定义了2个类型:SuperType()和SubType(),每个类型分别有一个属性和方法。2者的区别是SubType继承了
// SuperType。通过创建SuperType实例并将该实例赋给SubType.prototype。原来存在于SuperType中所有的属性和方法
// 也都存在于SubType.prototype中了。在确立了这种继承关系后,又在SubType.prototype中添加了一个方法。
1.别忘记默认的原型
所有引用类型默认都继承了Object,这个继承也是通过原型链实现的
大家要记住,所有函数的默认原型都是Object的实例,因此默认原型都会包含一个内部指针,指向Object.prototype
SubType继承了SuperType,而SuperType继承了Object,当调用instance.toString()时,实际上调用的是
保存在Object.prototype中的那个方法
注:实际上,不是SubType的constructor属性被重写了,而是SubType的原型指向了另一个对象---SuperType的原型
而这个原型对象的constructor属性指向的是SuperType。
2.确定原型和实例的关系
有两种方法确定原型和实例的关系
第一种方法是使用instanceof操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回true.
console.log(instance instanceof Object); // true
console.log(instance instanceof SuperType); // true
console.log(instance instanceof SubType); // true
第二种方法是使用isPrototypeOf()方法,同样,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型
console.log(Object.prototype.isPrototypeOf(instance)); // true
console.log(SuperType.prototype.isPrototypeOf(instance)); // true
console.log(SubType.prototype.isPrototypeOf(instance)); // true
3.谨慎的定义方法
function SuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
}
function SubType(){
this.subproperty=false;
}
// 继承了 SuperType
SubType.prototype=new SuperType();
// 添加新方法
SubType.prototype.getSubValue=function(){
return this.subproperty;
}
// 重写超类中的方法
SubType.prototype.getSuperValue=function(){
return false;
}
var instance=new SubType();
console.log(instance.getSuperValue()); // false
// 重写超类中的方法会屏蔽原来的方法,换句话说,当通过SubType的实例调用getSuperValue(),调用的是这个重新定义的方法
// 但通过SuperType的实例调用getSuperValue(),还会调用原来的那个方法。
// 需要格外注意的是:必须用SuperType的实例替换原型之后,再定义这个方法。
// 还有一点需要注意的是,在通过原型链继承的时候,不能使用对象字面量创建原型方法,因为这样做就会重写原型链。
// 如下面的例子会报错
function SuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
}
function SubType(){
this.subproperty=false;
}
// 继承了 SuperType
SubType.prototype=new SuperType();
// 使用字面量添加新方法,会导致上一行代码无效
SubType.prototype={
getSubValue:function(){
return this.subproperty;
},
someOtherMethods:function(){
return false;
}
}
var instance=new SubType();
console.log(instance.getSuperValue()); // instance.getSuperValue is not a function
4.原型链的问题
// 最主要的问题来自包含引用类型值的原型,包含引用类型值的原型属性会被所有实例共享,这也是为什么要在构造函数中,
// 而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性
// 也就顺理成章的变成了现在的原型属性了。
function SuperType(){
this.colors=["red","green","blue"];
}
function SubType(){
}
// 继承了SuperType
SubType.prototype=new SuperType();
var instance1=new SubType();
instance1.colors.push("yellow");
console.log(instance1.colors); // ["red", "green", "blue", "yellow"]
var instance2=new SubType();
console.log(instance2.colors); // ["red", "green", "blue", "yellow"]
// 在这个例子中,SubType的所有实例都共享这一个colors属性
// 原型链的第二个问题,在创建子类型的实例时,不能向构造函数中传递参数
// 实际上应该说,是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。