ES5 寄生组合式继承
手写 ES5 寄生组合式继承的实现
问题
手写 ES5 继承,使用寄生组合式继承方式实现。
解答
为什么是寄生组合式继承?
ES5 有多种继承方式,寄生组合式继承是最优解,因为它:
- 只调用一次父类构造函数
- 原型链保持完整
- 能正常使用
instanceof
完整实现
// 父类
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
// 子类
function Child(name, age) {
// 继承实例属性(调用父类构造函数)
Parent.call(this, name);
this.age = age;
}
// 继承原型方法(核心代码)
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// 子类自己的方法
Child.prototype.sayAge = function () {
console.log(this.age);
};
// 测试
const child1 = new Child('Tom', 18);
const child2 = new Child('Jerry', 20);
child1.colors.push('green');
console.log(child1.colors); // ['red', 'blue', 'green']
console.log(child2.colors); // ['red', 'blue']
child1.sayName(); // Tom
child1.sayAge(); // 18
console.log(child1 instanceof Child); // true
console.log(child1 instanceof Parent); // true
封装成工具函数
function inheritPrototype(Child, Parent) {
// 创建父类原型的副本
const prototype = Object.create(Parent.prototype);
// 修复 constructor 指向
prototype.constructor = Child;
// 赋值给子类原型
Child.prototype = prototype;
}
// 使用
function Parent(name) {
this.name = name;
}
Parent.prototype.sayName = function () {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inheritPrototype(Child, Parent);
Child.prototype.sayAge = function () {
console.log(this.age);
};
对比组合继承的问题
// 组合继承(有缺陷)
function Child(name, age) {
Parent.call(this, name); // 第二次调用 Parent
this.age = age;
}
Child.prototype = new Parent(); // 第一次调用 Parent(多余)
Child.prototype.constructor = Child;
组合继承调用了两次父类构造函数,且 Child.prototype 上会有多余的父类实例属性。
关键点
Parent.call(this)继承实例属性,确保每个实例有独立的引用类型属性Object.create(Parent.prototype)创建原型副本,避免直接new Parent()带来的副作用- 必须修复
constructor指向,否则Child.prototype.constructor会指向Parent - 相比组合继承,父类构造函数只调用一次,效率更高
- ES6 的
class extends本质上就是寄生组合式继承的语法糖
目录