简单回顾一下构造函数、原型和实例之间的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果如何?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念。
其代码的大致形式如下:
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subProperty = true;
}
// 继承 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subProperty;
}
var instance = new SubType();
console.log(instance.getSuperValue());
输出结果:
现在,原来存在于 SuperType 实例中的所有属性和方法,现在也存在于 SubType.prototype 中了。
注意: 在通过原型链实现继承时,不要使用对象字面量创建原型方法。因为这样做就会重写原型链。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subProperty = true;
}
// 继承 SuperType
SubType.prototype = new SuperType();
// 使用对象字面量创建原型方法,会导致上一行代码无效
SubType.prototype = {
getSubValue: function() {
return this.subProperty;
},
someOtherValue: function() {
return false;
}
}
var instance = new SubType();
console.log(instance.getSuperValue()); // error
原型链的问题
下面的例子可以充分展示原型链的第一个问题:
function SuperColors() {
this.colors = ["red", "green", "blue"];
}
function SubColors() {
}
// 继承 SuperColors
SubColors.prototype = new SuperColors();
var instance1 = new SubColors();
instance1.colors.push("white");
console.log(instance1.colors);
var instance2 = new SubColors();
console.log(instance2.colors);
输出结果:
原型链的第二个问题是:在创建子类型的实例时,不能向父类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给父类型的构造函数传递参数。