原型链
其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?
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;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
以上代码定义了两个类型: SuperType
和 SubType
。每个类型分别有一个属性和一个方法。它们的主要区别是 SubType
继承了 SuperType
,而继承是通过创建 SuperType
的实例,并将该实例赋给 SubType.prototype
实现的。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于 SuperType
的实例中的所有属性和方法,现在也存在于 SubType.prototype
中了。
原型链的问题
原先的实例属性也就顺理成章地变成了现在的原型属性了。
原型链的第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
借用构造函数
在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数的技术(有时候也叫做伪造对象或经典继承)。这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。别忘了,函数只不过是在特定环境中执行代码的对象,因此通过使用 apply()
和 call()
方法也可以在(将来)新创建的对象上执行构造函数,如下所示:
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push(“black”);
alert(instance1.colors); //“red,blue,green,black”
var instance2 = new SubType();
alert(instance2.colors); //“red,blue,green”
通过使用 call()
方法(或 apply()
方法也可以),我们实际上是在(未来将要)新创建的 SubType
实例的环境下调用了 SuperType
构造函数。这样一来,就会在新 SubType
对象上执行 SuperType()
函数中定义的所有对象初始化代码。结果, SubType
的每个实例就都会具有自己的 colors
属性的副本了。
传递参数
相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数。
借用构造函数的问题
如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。
组合继承
组合继承,有时候也叫做伪经典继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。
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;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType(“Nicholas”, 29);
instance1.colors.push(“black”);
alert(instance1.colors); //“red,blue,green,black”
instance1.sayName(); //“Nicholas”;
instance1.sayAge(); //29
var instance2 = new SubType(“Greg”, 27);
alert(instance2.colors); //“red,blue,green”
instance2.sayName(); //“Greg”;
instance2.sayAge(); //27
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JavaScript中最常用的继承模式。而且, instanceof
和 isPrototypeOf()
也能够用于识别基于组合继承创建的对象。
完