许多OO语言都支持两种继承方式:
- 接口继承
- 实现继承
接口继承只继承方法签名,实现继承则继承实际的方法。由于ECMAScript没有函数签名,无法实现接口继承,只支持实现继承。
ECMAScript实现继承主要依靠原型链来实现
原型链继承
- 创建一个父类的实例对象
- 令子类的构造函数的prototype属性指向步骤一中创建的父类实例对象
function Super(name) {
this.name = name;
this.sayName = function() {
alert(this.name);
};
}
function Subtype() {
}
//创建一个父类的实例对象
//子类的构造函数的prototype属性指向父类的实例对象
Subtype.prototype = new Super("Bob");
var subtype = new Subtype();
subtype.sayName();
原型链继承有两个缺点:
- 父类实例对象属性被所有子类实例共享
- 无法向超类构造函数传递参数
借用构造函数
我们知道,当用new操作符调用构造函数创建对象时,会将this绑定到这个新创建的对象上。借用构造函数即在构造函数中调用父类的构造函数,父类中的方法和属性都会绑定到子类对象上。
function Super(name) {
this.name = name;
this.sayName = function() {
alert(this.name);
};
}
function Subtype() {
Super.call(this,"Tom");
}
var subtype = new Subtype();
subtype.sayName();
alert(subtype instanceof Subtype); // true
alert(subtype instanceof Super); // false
借用构造函数模式虽然可以向超类构造函数传递参数,每个子类对象都从超类哪里获得了一份属性拷贝,但是它也存在两个缺点:
- 每个子类对象都需要重复创建超类方法对象
- 它是假继承,子类和超类没有继承关系,instanceof操作符失效
组合继承
组合继承的重点是:
- 将子类需要继承并且继承后变为实例属性的属性写入超类的构造函数中
- 超类方法通过原型写入
- 原型链继承
- 借用构造函数
function Super(name) {
this.name = name;
}
Super.prototype.sayName = function() {
alert(this.name);
};
function Subtype() {
Super.call(this,"Tom");
}
Subtype.prototype = new Super("Bob");
var subtype = new Subtype();
subtype.sayName();
alert(subtype instanceof Subtype);
alert(subtype instanceof Super);
我们首先通过组合原型模式与构造函数模式定义父类;子类使用原型链模式继承方法和确定继承关系,借用构造函数将父类属性变为实例属性。
这种模式实际上,子类原型(超类实例对象)的属性被子类中的同名属性屏蔽了,用子类访问这些属性时只会访问到实例属性
原型式继承
- 子类构造函数定义包含于函数中
- 外围函数接受一个超类对象
- 在外围函数中指定子类的原型
function outFunction(superObject) {
// 定义子类构造函数
function SubObject() {
}
SubObject.prototype = superObject;
return new SubObject();
}
function Test(name) {
this.name = name;
this.sayName = function() {
return this.name;
};
}
alert(outFunction(new Test("Tom")).sayName());
//alert(outFunction(new Test("Tom")) instanceof SubObject);
这种模式,无法在外围函数外识别对象类型。
寄生式继承
同工厂模式相似,利用函数封装继承过程。
寄生组合式继承
组合继承的缺点是会两次调用超类构造函数:
- 重写原型,确定继承关系时
- 借调构造函数,创建实例属性时
寄生组合继承在确定继承关系时,不使用实例超类对象重写原型的方式,而是通过重排原型、构造函数的内部属性指向来确定继承关系。
由于子类要继承超类,继承关系可用SubtypeConstructor.prototype = SupertypeConstructor.prototype
确定,不用实例化超类对象,自然也不用调用超类的构造函数。
function Super(name) {
this.name = name;
}
Super.prototype.sayName = function() {
alert(this.name);
};
function Subtype() {
Super.call(this,"Tom");
}
function inherit(Sub,Super) {
var prototype = Super.prototype;
//prototype.constructor =Sub;
Sub.prototype = prototype;
}
inherit(Subtype,Super);
var subtype = new Subtype();
subtype.sayName();
alert(subtype instanceof Subtype);
alert(subtype instanceof Super);