概述
首先说明,学习原型链的知识是为了更好的理解原型和原型对象,在实际应用中很少单独使用下面介绍的原型链,具体的原因是下面提到的它的缺点。
我们复习一下之前的知识:
- 构造函数的prototype指向了原型对象
- 原型对象中constructor指向了构造函数
- 实例中的__proto__指向了原型对象
这时候我们如果将一个实例的原型指针,指向另一个对象的实例,这时候这些实例就会串成一个链条,即A实例的__proto__指向B实例,B实例的__proto__又指向其原型对象。下面的代码是原型链的基本实现:
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType(); //将SubType的原型对象指向SuperType的实例
SubType.prototype.getSubValue = function() {
return this.subProperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); // true
其继承关系如下图
注意,SubType的prototype指向SuperType的一个实例,所以SubType的prototype的prototype就指向了SuperType的prototype。(理解清楚,有点绕口)
为了强化这个概念,我们看一个问题,在上面的代码中,instance的constructor属性指向谁呢?答案是指向SuperType的构造函数。为什么呢?我们来分析一下:
- 在原型链中SubType的prototype指向SuperType的一个实例
- 实例并没有constructor属性,这个属性是在prototype中的,原型对象中的这个属性指向了构造函数
- instance没有constructor,要从原型对象中获得constructor
- 其原型对象是SuperType的一个实例,这个实例也没有constructor,要从自己的prototype中获得
- 从SuperType的一个实例的prototype中获得的constructor就是SuperType的构造函数
默认原型Object
我们之前说过,如果写了一个构造函数,就会创建一个默认的原型对象,现在我们学习了原型链之后我们就能进一步完整这个理解。
创建的默认的原型对象的prototype指向Object,从Object中继承了toString ValueOf等方法。所以完整的原型链如下如:
注意SuperType的prototype指向Object,而SubType的prototype原本也指向Object,我们将其指向了Supertype的实例,实现了原型链。
注意在上面的图中,SubType Prototype其实就是SuperType的一个实例,所以它的Prototype才指向了SuperType的Prototype,同理,Supertype Prototype也是Object的一个实例。
对象识别
- instanceof 这个操作符可以判断实例的任意一个父构造函数
alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true
- isPrototypeOf 判断实例的原型,任意在链上的原型都可以
alert(Object.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true
复写方法
注意复写方法一定要在替换原型对象之后,这个也很好理解,因为我们在替换了原型对象之后,指针就改变了。
原型链的问题
- 实例不复写直接使用原型中的引用类型属性,这个属性会在所有的实例中共享。这是我们不希望看到的。
function SuperType() { this.property = ['red']; } function SubType() {} SubType.prototype = new SuperType(); //将SubType的原型对象指向SuperType的实例 var instance = new SubType(); instance.property.push('blue'); var ins2 = new SubType(); alert(ins2.property); //red,blue 无法各自拥有自己的,原先属于原型对象的引用类型
- 创建子类型的时候不能向超类型的构造函数中传递参数