组合式继承的不足
组合继承是JavaScript最常用的继承模式,但也有它的不足:
- 无论什么情况下,都会调用两次超类构造函数
- 子类会包含超类对象全部的实例属性,但又不得不在调用子类构造函数时重写这些属性
先看一看组合继承的例子:
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); // 第二次调用 SuperType()
this.age = age;
}
SubType.prototype = new SuperType(); // 第一次调用 SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
如代码所示,
- 在第一次调用 SuperType 构造函数SubType.prototype 会得到两个属性: name 和 colors ;它们都是 SuperType 的实例属性,只不过
现在位于 SubType 的原型中。- 当调用 SubType 构造函数时,又会调用一次 SuperType 构造函数,这一次又在新对象上创建了实例属性 name 和 colors 。于是,这两个属性就屏蔽了原型中的两个同名属性
解决方案
有两组 name 和 colors 属性:一组在实例上,一组在 SubType 原型中,这是不合理的。解决这个问题方法是:寄生组合式继承。
寄生组合式继承基本模式:
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = prototype; //指定对象
}
这个函数接收两个参数:子类型构造函数和超类型构造函数, 执行以下步骤:
- 第一步是创建超类型原型的一个副本。
- 为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性。
- 将新创建的对象(即副本)赋值给子类型的原型。
现在我们就可以用调用 inheritPrototype() 函数的语句,去替换前面例子中为子类型原型赋值的语句了,修改后的代码如下:
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;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
这个例子的高效率体现在它只调用了一次 SuperType 构造函数,并且因此避免了在 SubType.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和 isPrototypeOf() 。普遍认为寄生组合式继承是引用类型最理想的继承范式。
网友评论