寄生组合式继承是组合式继承的优化版本,解决了组合式继承中父类构造函数被调用两次的问题,是目前JavaScript中最理想的继承方式。
1. 核心实现原理
// 父类
function Parent(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
Parent.prototype.sayName = function() {
console.log('Parent name:', this.name)
}
// 子类
function Child(name, age) {
// 1. 构造函数继承(只调用一次父类构造函数)
Parent.call(this, name) // 关键点:只在这里调用一次
this.age = age
}
// 2. 原型继承(不使用 new Parent(),避免再次调用构造函数)
Child.prototype = Object.create(Parent.prototype) // 关键点:创建原型副本
Child.prototype.constructor = Child // 修复constructor指向
// 子类自己的方法
Child.prototype.sayAge = function() {
console.log('Child age:', this.age)
}
// 测试
const child1 = new Child('小明', 10)
child1.colors.push('yellow')
console.log(child1.colors) // ['red', 'blue', 'green', 'yellow']
child1.sayName() // Parent name: 小明
child1.sayAge() // Child age: 10
const child2 = new Child('小红', 8)
console.log(child2.colors) // ['red', 'blue', 'green']
2. 封装成通用函数
/**
* 实现寄生组合式继承
* @param {Function} Child 子类构造函数
* @param {Function} Parent 父类构造函数
*/
function inherit(Child, Parent) {
// 创建父类原型的副本
const prototype = Object.create(Parent.prototype)
// 修复constructor指向
prototype.constructor = Child
// 设置子类的原型
Child.prototype = prototype
}
// 使用示例
function Animal(name) {
this.name = name
}
Animal.prototype.sayName = function() {
console.log('I am ' + this.name)
}
function Dog(name, breed) {
Animal.call(this, name)
this.breed = breed
}
// 调用继承函数
inherit(Dog, Animal)
Dog.prototype.bark = function() {
console.log(this.name + ' says: Woof!')
}
// 测试
const dog = new Dog('Buddy', 'Golden Retriever')
dog.sayName() // I am Buddy
dog.bark() // Buddy says: Woof!
console.log(dog instanceof Dog) // true
console.log(dog instanceof Animal) // true
3. 更完善的继承函数(支持静态方法继承)
function extend(Child, Parent) {
// 继承原型方法
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
// 继承静态方法(ES5方式)
for (var key in Parent) {
if (Parent.hasOwnProperty(key)) {
Child[key] = Parent[key]
}
}
// ES6+ 方式(如果支持)
if (Object.setPrototypeOf) {
Object.setPrototypeOf(Child, Parent)
} else if (Child.__proto__) {
Child.__proto__ = Parent
}
return Child
}
// 使用示例
function Shape(color) {
this.color = color
}
// 实例方法
Shape.prototype.getColor = function() {
return this.color
}
// 静态方法
Shape.create = function(color) {
return new Shape(color)
}
function Circle(color, radius) {
Shape.call(this, color)
this.radius = radius
}
extend(Circle, Shape)
Circle.prototype.getArea = function() {
return Math.PI * this.radius * this.radius
}
// 测试
const circle = new Circle('red', 10)
console.log(circle.getColor()) // red
console.log(circle.getArea()) // 314.159...
// 静态方法也被继承
console.log(Circle.create) // [Function: create]
4. ES5兼容方案
// 兼容旧浏览器的Object.create
if (typeof Object.create !== 'function') {
Object.create = function(proto) {
function F() {}
F.prototype = proto
return new F()
}
}
// 通用继承函数(兼容版本)
function inheritPrototype(Child, Parent) {
// 创建原型副本
var prototype = Object.create(Parent.prototype)
prototype.constructor = Child
Child.prototype = prototype
}
// 使用示例
function Vehicle(type) {
this.type = type
}
Vehicle.prototype.move = function() {
console.log(this.type + ' is moving')
}
function Car(type, brand) {
Vehicle.call(this, type)
this.brand = brand
}
inheritPrototype(Car, Vehicle)
Car.prototype.honk = function() {
console.log(this.brand + ' honks!')
}
5. 与组合式继承的对比
// 组合式继承(有缺陷的版本)
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实例都会打印两次"Parent constructor called"
// 寄生组合式继承(优化版本)
function OptimizedChild(name, age) {
Parent.call(this, name) // 只调用一次 ✅
this.age = age
}
// 使用Object.create,不调用Parent构造函数
OptimizedChild.prototype = Object.create(Parent.prototype)
OptimizedChild.prototype.constructor = OptimizedChild
// 只打印一次"Parent constructor called"
`
## 6. 实际应用示例
```js
// 事件发射器基类
function EventEmitter() {
this._events = {}
}
EventEmitter.prototype.on = function(event, listener) {
if (!this._events[event]) {
this._events[event] = []
}
this._events[event].push(listener)
}
EventEmitter.prototype.emit = function(event, ...args) {
const listeners = this._events[event]
if (listeners) {
listeners.forEach(listener => listener.apply(this, args))
}
}
// 自定义组件
function Component(name) {
EventEmitter.call(this)
this.name = name
this.state = {}
}
// 寄生组合式继承
Component.prototype = Object.create(EventEmitter.prototype)
Component.prototype.constructor = Component
Component.prototype.setState = function(newState) {
Object.assign(this.state, newState)
this.emit('stateChanged', this.state)
}
// 使用
const comp = new Component('MyComponent')
comp.on('stateChanged', function(state) {
console.log('State changed:', state)
})
comp.setState({ loading: true }) // 触发事件
优点总结
- 只调用一次父类构造函数:效率更高
- 原型链保持纯净:没有多余的父类实例属性
- instanceof正常工作:原型链完整
- 可向父类传参:通过构造函数继承实现
- 可扩展性强:易于添加额外功能
寄生组合式继承是目前JavaScript中最完美的继承实现方式,也是ES6的class extends语法在底层的实现原理。