Welcome to wechat public account: Front Reading Room

introduce

In addition to the traditional object-oriented inheritance approach, a popular way to create classes from reusable components is to combine the code of another simple class. You may be familiar with mixins and their features in languages like Scala, but they are also popular in JavaScript.

With the sample

The following code demonstrates how to use mixin in TypeScript. We’ll explain how this code works later.

// Disposable Mixin
class Disposable {
    isDisposed: boolean;
    dispose() {
        this.isDisposed = true; }}// Activatable Mixin
class Activatable {
    isActive: boolean;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false; }}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;
}
applyMixins(SmartObject, [Disposable, Activatable]);

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

////////////////////////////////////////
// In your runtime library somewhere
////////////////////////////////////////

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

Understand this example

The code first defines two classes that will serve as mixins. You can see that each class defines only one specific behavior or function. We’ll use them later to create a new class that does both.

// Disposable Mixin
class Disposable {
    isDisposed: boolean;
    dispose() {
        this.isDisposed = true; }}// Activatable Mixin
class Activatable {
    isActive: boolean;
    activate() {
        this.isActive = true;
    }
    deactivate() {
        this.isActive = false; }}Copy the code

Let’s create a class that combines these two mixins. Here’s how it works:

class SmartObject implements Disposable.Activatable {
Copy the code

The first thing you should notice is that implements is used instead of extends. The class is treated as an interface, using only the types of Disposable and Activatable instead of their implementations. This means that we need to implement interfaces in our classes. But that’s what we want to avoid when we use mixins.

We can do this by creating placeholder properties for the property methods that will be mixin. This tells the compiler that these members are available at run time. This allows you to take advantage of mixins, although some placeholder attributes need to be defined in advance.

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

Finally, mixins are mixed into the defined classes to complete the implementation.

applyMixins(SmartObject, [Disposable, Activatable]);
Copy the code

Finally, create this helper function to help us with blending. It iterates over all the mixins’ properties and copies them to the target, replacing the placeholder properties with the actual implementation code.

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

Welcome to wechat public account: Front Reading Room