混合

在使用传统的面向对象语言时,一个基于类构建可重用组件的办法是,让一个类基于多个简单的类。你可能对Scala语言中“混合”的概念有所知晓。类似的概念如今在JavaScript社区中也越来越流行。

关于混合的例子

下面的例子展示了TypeScript中的混合:

  1. // Disposable Mixin
  2. class Disposable {
  3. isDisposed: boolean;
  4. dispose() {
  5. this.isDisposed = true;
  6. }
  7. }
  8. // Activatable Mixin
  9. class Activatable {
  10. isActive: boolean;
  11. activate() {
  12. this.isActive = true;
  13. }
  14. deactivate() {
  15. this.isActive = false;
  16. }
  17. }
  18. class SmartObject implements Disposable, Activatable {
  19. constructor() {
  20. setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
  21. }
  22. interact() {
  23. this.activate();
  24. }
  25. // Disposable
  26. isDisposed: boolean = false;
  27. dispose: () => void;
  28. // Activatable
  29. isActive: boolean = false;
  30. activate: () => void;
  31. deactivate: () => void;
  32. }
  33. applyMixins(SmartObject, [Disposable, Activatable])
  34. var smartObj = new SmartObject();
  35. setTimeout(() => smartObj.interact(), 1000);
  36. ////////////////////////////////////////
  37. // In your runtime library somewhere
  38. ////////////////////////////////////////
  39. function applyMixins(derivedCtor: any, baseCtors: any[]) {
  40. baseCtors.forEach(baseCtor => {
  41. Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
  42. derivedCtor.prototype[name] = baseCtor.prototype[name];
  43. })
  44. });
  45. }

理解例子

上文的例子以两个类开始,这两个类专注于各自的活动和功能。在后面我们将它们混合入一个新类来同时获取它们两个的功能。

  1. // Disposable Mixin
  2. class Disposable {
  3. isDisposed: boolean;
  4. dispose() {
  5. this.isDisposed = true;
  6. }
  7. }
  8. // Activatable Mixin
  9. class Activatable {
  10. isActive: boolean;
  11. activate() {
  12. this.isActive = true;
  13. }
  14. deactivate() {
  15. this.isActive = false;
  16. }
  17. }

然后,我们将这个类混合入了一个新类,让我们仔细看看它的语法:

  1. class SmartObject implements Disposable, Activatable {

你可能很快注意到,我们并没有使用extends关键字,而是使用了implements。这会将类视为接口,然后只使用DisposableActivatable这两个类背后的类型声明,而不是具体实现。这意味着在后面,我们需要自己在类中提供实现细节。这可能并不是我们喜欢做的。

为了不重新提供实现细节,我们创建了同名属性,并且提供了同样的类型。这会告诉编译器这些成员在运行时将会可用,这将让我们不用机械地重新抄写一遍同样的代码:

  1. // Disposable
  2. isDisposed: boolean = false;
  3. dispose: () => void;
  4. // Activatable
  5. isActive: boolean = false;
  6. activate: () => void;
  7. deactivate: () => void;

最后,我们将其混合入类中,补全了具体的实现:

  1. applyMixins(SmartObject, [Disposable, Activatable])

applyMixins是我们创建的一个辅助函数。这将会遍历我们被混合的类的原型,将其的具体实现赋值给最终类:

  1. function applyMixins(derivedCtor: any, baseCtors: any[]) {
  2. baseCtors.forEach(baseCtor => {
  3. Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
  4. derivedCtor.prototype[name] = baseCtor.prototype[name];
  5. })
  6. });
  7. }