一、介绍

除了传统的面向对象继承方式,还流行一种通过可重用组件创建类的方式,就是联合另一个简单类的代码。

二、Mixins 示例

下面代码演示了如何在 TypeScript 中使用 Mixins:

// disposable Mixin
class Disposable {
    isDisposabled: boolean;
    dispose() {
        this.isDisposabled = true;
    }
}

// Activatable Mixin
class Activatable {
    isActive: boolean;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false;
    }
}

class SmartObject implements Disposable, Activatable {
    constructor() {
        setInterval(() => {
            console.log(`isActive: ${this.isActive} - isDisposabled: ${this.isDisposabled}`);
        }, 500);
    }
    interact() {
        this.activate();
    }
    // Disposable
    isDisposabled: boolean = false;
    dispose: () => void;
    // Activatable
    isActive: boolean = false;
    activate: () => void;
    deactivate: () => void;
}

applyMixins(SmartObject, [Disposable, Activatable]);

let smartObj = new SmartObject();
setTimeout(() => {
    smartObj.interact();
}, 1000);


// library
function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name]
        });
    });
}

编译结果:

// disposable Mixin
var Disposable = /** @class */ (function () {
    function Disposable() {
    }
    Disposable.prototype.dispose = function () {
        this.isDisposabled = true;
    };
    return Disposable;
}());
// Activatable Mixin
var Activatable = /** @class */ (function () {
    function Activatable() {
    }
    Activatable.prototype.activate = function () {
        this.isActive = true;
    };
    Activatable.prototype.deactivate = function () {
        this.isActive = false;
    };
    return Activatable;
}());
var SmartObject = /** @class */ (function () {
    function SmartObject() {
        var _this = this;
        // Disposable
        this.isDisposabled = false;
        // Activatable
        this.isActive = false;
        setInterval(function () {
            console.log("isActive: " + _this.isActive + " - isDisposabled: " + _this.isDisposabled);
        }, 500);
    }
    SmartObject.prototype.interact = function () {
        this.activate();
    };
    return SmartObject;
}());
applyMixins(SmartObject, [Disposable, Activatable]);
var smartObj = new SmartObject();
setTimeout(function () {
    smartObj.interact();
}, 1000);
// library
function applyMixins(derivedCtor, baseCtors) {
    baseCtors.forEach(function (baseCtor) {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(function (name) {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
}

运行结果:

1.jpg

三、理解上面的例子

上面代码中首先定义了两个类,它们就是 mixins,可以看到每个类都只定义了一个特定的行为或者功能。

// Disposable Mixin
class Disposable {
    isDisposed: boolean;
    dispose() {
        this.isDisposed = true;
    }

}

// Activatable Mixin
class Activatable {
    isActive: boolean;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false;
    }
}

下面创建一个类,结合了这两个 minxins,具体实现如下:

class smartObject implements Disposable, Activatable {}

首先应该注意的是,没有使用 extends 而是使用 implements。这里把 class 当做了 interface ,仅使用 Disposable 和 Activatable 的类型,并不使用它的实现。

因此在 SmartObject 类中实现接口,但是我们却希望在使用 minxin 的时候避免这种行为。

下面这种方式能够达到这种实现接口的目的,为将要 mixin 进行来的属性方法创建占位属性。这告诉编译器字儿写成员在运行时是可用的。这样就能使用 mixin 带来的便利,虽然说这样需要提前定义一些占位属性。

// Disposable
isDisposed: boolean = false;
dispose: () => void;
// Activatable
isActive: boolean = false;
activate: () => void;
deactivate: () => void;

最后,把mixins混入定义的类,完成全部实现部分。

applyMixins(SmartObject, [Disposable, Activatable]);

最后,创建这个帮助函数,帮我们做混入操作。 它会遍历mixins上的所有属性,并复制到目标上去,把之前的占位属性替换成真正的实现代码。

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        })
    });
}