许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。ECMAScript只支持实现继承,而且其实现继承主要是依赖原型链来实现的。
以一个例子来说明一些知识:
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承 SuperType
SubType.prototype = new SuperType(); //new了一个SuperType类的实例
SubType.prototype.getSubValue = function(){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue());
确定原型和实例的关系
1.使用instanceof
只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回true
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
2.使用isPrototypeOf()
只要是原型链中出现过的原型,都可以说是该原型链所派生的实例原型。
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true
原型链的问题
1.包含引用类型值所带来的问题。例如
function SuperType(){
this.colors = ["a", "b", "c"];
}
function SubType(){
}
//继承了SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("d");
alert(instance1.colors); //"a,b,c,d"
var instance2 = new SubType();
alert(instance2.colors); //"a,b,c,d"
结果是SubType的所有实例都会共享同一个colors属性,这不是我们想要的。
2.在创建子类型的实例时,不能向超类型的构造函数中传递参数。
以上两个问题都可以使用“借用构造函数”(也称伪造对象或经典继承)来解决。
借用构造函数
- 使用apply()和call()方法在新创建的对象上执行构造函数。重写上面的例子
function SuperType(){
this.colors = ["a", "b", "c"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
//SuperType.apply(this);
}
var instance1 = new SubType();
instance1.colors.push("d");
alert(instance1.colors); //"a,b,c,d"
var instance2 = new SubType();
alert(instance2.colors); //"a,b,c"
- 借用构造函数的问题
回到了构造函数模式,函数无法实现复用。
组合继承
也叫作伪经典继承。使用原型链实现对原型属性和方法的继承,通过借用函数来实现对实例属性的继承。
function SuperType(name){
this.name = name;
this.colors = ["a", "b", "c"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name,age){
//继承属性
SuperType.call(this,name);
this.age = age;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("d");
alert(instance1.colors); //"a,b,c,d"
instance1.sayName(); //"Nicholas"
instance2.sayAge(); //29
var instance2 = new SubType("Greg",27);
alert(instance2.colors); //"a,b,c"
instance1.sayName(); //"Greg"
instance2.sayAge(); //27
原型式继承
ECMAScript5通过新增Object.create()方法规范化了原型式继承。该方法接受两个参数;
- 用作新对象原型的对象
- (可选)为新对象定义额外属性的对象
var person = {
name: "Nicholas",
friends: ["Shelby", "Count", "Van"]
};
var anotherPerson = Object.create(person,{
name:{
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"
注意:使用引用类型值的属性始终都会共享相应的值
寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回对象。
最理想的继承范式——寄生组合式继承
本质上是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下:
function inheritPropotype(subType,superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
该示例函数实现了寄生组合式继承最简单的形式。该函数接受两个参数:子类型构造函数和超类型构造函数。在函数内部:
1.创建超类型原型的一个副本
2.为创建的副本添加constructor属性,从而弥补因为重写原型而失去的默认constructor属性
3.将新创建的对象(即副本)赋值给子类型的原型。
function SuperType(name){
this.name = name;
this.colors = ["a", "b", "c"];
}
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);
}
网友评论