JS 的组合式继承 为什么父构造函数会执行两次
组合式继承(也称为伪经典继承)是 JavaScript 中一种常见的继承方式,它结合了原型链继承和构造函数继承的优点。组合式继承的主要步骤包括:
然而,这种继承方式有一个问题:父类的构造函数会被调用两次。让我们通过一个示例来详细解释这个问题。
// 父类构造函数
function Parent(name) {
this.name = name;
console.log('Parent constructor called');
}
// 父类原型方法
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 子类构造函数
function Child(name, age) {
// 第一次调用父类构造函数
Parent.call(this, name);
this.age = age;
}
// 子类原型继承父类原型
Child.prototype = new Parent(); // 第二次调用父类构造函数
Child.prototype.constructor = Child;
// 子类原型方法
Child.prototype.sayAge = function() {
console.log(this.age);
};
// 测试
const child = new Child('John', 25);
child.sayName(); // John
child.sayAge(); // 25
在上面的代码中,父类构造函数 Parent
被调用了两次:
Child
构造函数内部,通过 Parent.call(this, name)
来调用父类构造函数。这是为了确保子类实例能够继承父类的实例属性。Child.prototype = new Parent();
这行代码中。这里通过创建一个新的 Parent
实例来设置子类的原型,这样子类就能继承父类的原型方法。这种方式虽然实现了继承,但也带来了性能上的开销,因为父类构造函数被调用了两次。
为了避免父类构造函数被调用两次,可以使用寄生组合式继承。这种方式通过创建一个中间对象来继承父类的原型,���不是直接调用父类构造函数。
// 父类构造函数
function Parent(name) {
this.name = name;
console.log('Parent constructor called');
}
// 父类原型方法
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 子类构造函数
function Child(name, age) {
Parent.call(this, name); // 只调用一次父类构造函数
this.age = age;
}
// 创建一个中间对象来继承父类的原型
function inheritPrototype(child, parent) {
const prototype = Object.create(parent.prototype); // 创建父类原型的一个副本
prototype.constructor = child; // 修正子类构造函数的指向
child.prototype = prototype; // 将子类的原型指向这个副本
}
// 使用寄生组合式继承
inheritPrototype(Child, Parent);
// 子类原型方法
Child.prototype.sayAge = function() {
console.log(this.age);
};
// 测试
const child = new Child('John', 25);
child.sayName(); // John
child.sayAge(); // 25
在这个改进的版本中,父类构造函数只被调用了一次,从而避免了不必要的性能