概念
Mixins 是一种软件设计模式,在 JavaScript 中用于实现代码复用和组合功能。它允许将多个不同的行为或特性组合到一个类中,而无需使用传统的类继承。通过 Mixins,可以从多个源获取功能,使代码更加灵活和可维护。
与传统面向对象继承的区别
- 传统继承
- 通常是单一的层次结构,一个类只能从一个父类继承。
- 继承关系较为严格,可能导致复杂的类层次结构和较高的耦合度。
- Mixins
- 可以从多个不同的源组合功能,更加灵活。
- 不是基于严格的父子关系,而是通过合并代码来实现功能共享。
示例
定义两个混入类:
// Disposable Mixin
class Disposable {
isDisposed: boolean = false;
dispose() {
this.isDisposed = true;
}
}
// Activatable Mixin
class Activatable {
isActive: boolean = false;
activate() {
this.isActive = true;
}
deactivate() {
this.isActive = false;
}
}
定义主类 SmartObject
:
class SmartObject implements Disposable, Activatable {
constructor() {
setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
}
interact() {
this.activate();
}
// Disposable
isDisposed: boolean = false;
dispose: () => void;
// Activatable
isActive: boolean = false;
activate: () => void;
deactivate: () => void;
}
注意:这里使用的是 implements
关键字,而不是extends
。
- 类
SmartObject
的目的是:组合Disposable
和Activatable
的功能。 - 通过
implements
关键字声明实现这两个 Mixins,表明它应该具有这两个 Mixins 中的所有属性和方法。
把类当成了接口,仅使用Disposable
和Activatable
的类型而非其实现。 这意味着需要在类里面实现接口。 但是这是在用mixin时想避免的。 - 对于
Disposable
和Activatable
中的属性和方法,进行了类型声明,但没有具体实现。
这是为将要mixin进来的属性方法创建出占位属性。
创建混入函数applyMixins
做混入操作。 它会遍历mixins上的所有属性,并复制到目标上去,把之前的占位属性替换成真正的实现代码。
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
- 这是一个通用的 Mixins 应用函数。
- 接受两个参数,
derivedCtor
表示要应用 Mixins 的派生类构造函数(这里是SmartObject
),baseCtors
是一个包含要混入的基类构造函数的数组(这里是[Disposable, Activatable]
)。 - 遍历
baseCtors
数组中的每个基类构造函数:- 通过
Object.getOwnPropertyNames(baseCtor.prototype)
获取基类原型上的所有自有属性名。 - 对于每个属性名,将基类原型上的对应属性赋值给派生类的原型,从而实现了将 Mixins 的功能添加到派生类上。
- 通过
最后,把mixins混入定义的类,完成全部实现部分。
applyMixins(SmartObject, [Disposable, Activatable]);
// 创建 SmartObject 的实例 smartObj
let smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000);