Mixins in JavaScript.

Author
By Darío Rivera
Posted On in Javascript

Mixins in JavaScript are a widely used design pattern that allows you to perform horizontal programming by providing functions across multiple classes or objects. Let's take a closer look at this pattern with a practical example.

Scenario

Suppose we have a class that validates if a number is a digit.

If you have not yet had contact with JavaScript classes, we recommend that you see our article Classes in JavaScript and Introduction to Object-Oriented Programming.

class Digits {
  RegExpr = /^\d*$/;
  messages = {};

  constructor(value) {
    this.value = value;
  }

  isValid() {
    if (!this.value.match(this.RegExpr)) {
      this.messages.notDigits = 'The input must contain only digits';
      return false;
    }

    return true;
  }

  getMessages() {
    return this.messages;
  }
}

To verify if a string is numeric, we could do something like:

const digits = new Digits('a');
digits.isValid();
digits.getMessages();  // {notDigits: 'The input must contain only digits'}

Now suppose we have another class that validates an alphanumeric string.

class Alnum {
  RegExpr = /^(\d|[a-zA-Z]|\s)*$/;
  messages = {};

  constructor(value) {
    this.value = value;
  }

  isValid() {
    if (!this.value.match(this.RegExpr)) {
      this.messages.notAlnum = 'The input contains characters which are non alphabetic and no digits';
      return false;
    }

    return true;
  }

  getMessages() {
    return this.messages;
  }
}

To verify if a string is alphanumeric, we could do something like:

const alnum = new Alnum('añ/45@');
alnum.isValid();
alnum.getMessages();

Use of mixins

In the previous example, you can notice that both classes use the method getMessages(). Initially, one could think of creating an abstract class and extending these two validators from it. However, this method is a bit more generic and could be used not only in validators but in any other type of objects. That's why the appropriate solution is for this behavior to be horizontal and not vertical.

To achieve this, we just need to remove the getMessages() method from each class and create the following object.

const HasMessages = {
  getMessages() {
    return this.messages;
  },
};

Once this is done, we must assign this object to the prototype of each class.

Object.assign(Alnum.prototype, HasMessages);
Object.assign(Digits.prototype, HasMessages);

If you use ES modules in your application, this statement could go in each class file just before the export. This way you are assigning said method to be used by both classes without repeating the code and allowing it to be used in other types of entities.

It is also possible to assign the mixin in the constructor function. This assignment method has the advantage of having all the method definitions in one place.

constructor(value) {
  this.value = value;
  Object.assign(this, HasMessages);
}

We can even go a step further and assign the method directly to the prototype (we have used suspension points to omit code that is not important for the example).

class Alnum {
...
  constructor(value) {
    this.value = value;
    Object.assign(this, HasMessages);
  }
...
}

const HasMessageMixin = {
  __proto__: {
    getMessages() {
      return this.messages;
    },
  },
  getMessages() {
    return super.getMessages()
  }
}

Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.