了解!你问的 “ABP-159,JS最理想的继承——寄生组合式继承”,我帮你详细讲讲寄生组合式继承的原理、代码示例,以及为什么它被认为是JavaScript中“最理想”的继承模式。
一、背景:JavaScript继承方式简述
JavaScript中的继承方式主要有:
- 原型链继承
- 借用构造函数继承(经典继承)
- 组合继承(原型链 + 构造函数)
- 寄生继承
- 寄生组合继承(最理想)
二、寄生组合式继承介绍
寄生组合式继承结合了原型链继承和借用构造函数继承的优点,避免了组合继承的两次调用父构造函数的缺点,性能更好,结构更清晰。
- 核心思想:通过一个“寄生”的辅助函数,把父类的原型对象作为子类的原型,但不执行父类构造函数,避免多次构造。
- 子类构造函数中借用父类构造函数继承实例属性。
- 子类原型指向父类原型的一个副本,实现原型链继承。
三、实现步骤及代码示例
// 父类构造函数
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 创建一个对象,作为子类原型的原型
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
// 寄生组合继承核心方法
function inheritPrototype(SubType, SuperType) {
const prototype = object(SuperType.prototype); // 创建父类原型的副本
prototype.constructor = SubType; // 修正constructor指向子类
SubType.prototype = prototype; // 指定子类原型
}
// 子类构造函数
function Child(name, age) {
Parent.call(this, name); // 借用构造函数,继承实例属性
this.age = age;
}
// 继承父类原型方法
inheritPrototype(Child, Parent);
Child.prototype.sayAge = function() {
console.log(this.age);
};
// 测试
const child1 = new Child('Alice', 20);
child1.colors.push('yellow');
child1.sayName(); // Alice
child1.sayAge(); // 20
console.log(child1.colors); // ['red', 'blue', 'green', 'yellow']
const child2 = new Child('Bob', 18);
child2.sayName(); // Bob
child2.sayAge(); // 18
console.log(child2.colors); // ['red', 'blue', 'green']
四、为什么寄生组合继承是最理想?
继承方式 | 优点 | 缺点 |
---|---|---|
原型链继承 | 简单实现原型共享 | 无法传递参数,所有实例共享引用属性 |
借用构造函数继承 | 解决实例属性共享问题,可传参数 | 无法继承原型方法,方法复用差 |
组合继承 | 综合两者优点,属性和方法继承 | 父类构造函数调用两次,效率低 |
寄生组合继承 | 解决组合继承缺点,效率高,结构清晰 | 代码稍复杂 |
五、总结
- 寄生组合继承实现了性能与复用的最佳平衡
- 通过
inheritPrototype
辅助函数避免父类构造函数多次调用 - 保证实例属性独立且方法共享
- 是ES5及以前版本推荐的继承方式
好嘞!下面给你详细写个 寄生组合继承 和 ES6 class继承 的对比讲解,帮你理解两者设计思想、实现细节和性能差异。
一、寄生组合继承 (ES5写法)
// 父类构造函数
function Parent(name) {
this.name = name;
this.colors = ['red', 'green', 'blue'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
// 创建一个新对象,做原型继承
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
// 寄生组合继承的辅助函数
function inheritPrototype(SubType, SuperType) {
const prototype = object(SuperType.prototype); // 创建父类原型的副本
prototype.constructor = SubType; // 修正constructor指向
SubType.prototype = prototype; // 指定子类原型
}
// 子类构造函数
function Child(name, age) {
Parent.call(this, name); // 继承实例属性
this.age = age;
}
// 继承父类原型方法
inheritPrototype(Child, Parent);
Child.prototype.sayAge = function() {
console.log(this.age);
};
// 测试
const child1 = new Child('Alice', 25);
child1.colors.push('yellow');
child1.sayName(); // Alice
child1.sayAge(); // 25
console.log(child1.colors); // ['red', 'green', 'blue', 'yellow']
const child2 = new Child('Bob', 20);
child2.sayName(); // Bob
child2.sayAge(); // 20
console.log(child2.colors); // ['red', 'green', 'blue']
特点总结:
Parent.call(this, name)
实现了实例属性继承,确保每个实例有独立的属性副本inheritPrototype
实现原型链继承,避免了组合继承调用父构造函数两次的性能问题- 实现了方法共享和实例属性独立的完美结合
二、ES6 Class 继承实现
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'green', 'blue'];
}
sayName() {
console.log(this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // 调用父类构造函数,继承实例属性
this.age = age;
}
sayAge() {
console.log(this.age);
}
}
// 测试
const child1 = new Child('Alice', 25);
child1.colors.push('yellow');
child1.sayName(); // Alice
child1.sayAge(); // 25
console.log(child1.colors); // ['red', 'green', 'blue', 'yellow']
const child2 = new Child('Bob', 20);
child2.sayName(); // Bob
child2.sayAge(); // 20
console.log(child2.colors); // ['red', 'green', 'blue']
特点总结:
extends
关键字自动实现了原型链继承super()
负责调用父类构造函数,继承实例属性- 语法更简洁、语义明确
- 内部实现本质上也是寄生组合继承的思路,只是语法糖
三、对比总结
方面 | 寄生组合继承 (ES5) | ES6 Class继承 |
---|---|---|
语法 | 复杂,需辅助函数和手动设置原型 | 简洁,语法糖自动完成 |
性能 | 避免父构造函数调用两次,性能较优 | 类似寄生组合继承,性能无明显差异 |
易用性 | 初学者较难理解 | 简洁明了,易于学习 |
兼容性 | 兼容ES5环境 | 需要ES6环境或转译 |
原理 | 手动构建原型链,调用父构造函数 | 语言层面封装继承逻辑 |
四、总结
- 寄生组合继承是ES5时代实现继承的最优方案,解决了组合继承调用两次父构造函数的性能问题
- ES6 class继承 是对寄生组合继承的语法糖封装,语法更加优雅,适合现代JS开发
- 了解寄生组合继承有助于深入理解JavaScript继承机制本质
- 实际项目建议使用ES6 class继承,代码更清晰易维护
如果需要,我还能帮你:
- 深入解析原型链继承、组合继承、寄生继承等其它方式的优缺点和实现代码
- 做寄生组合继承的性能测试对比
- 演示TypeScript中继承的细节
随时说!
发表回复