What is mixin in JavaScript?

Overview

A mixin is a style of programming in which a class will include all the features (attributes and methods) of some other class without necessarily being related to that class.

Note: This programming is different from inheritance. That is why we used the word "include" and not "inheritance."

In inheritance, the child class will inherit all the features of its parent class and become a derivative or type of that class. Usually, if we want to obtain the features of multiple classes, we'll have to go through complex inheritance patterns, which can make our code confusing.

Luckily, mixins allow us to access all features from another class while avoiding complex inheritance problems.

Mixins also come in handy when there is a common functionality between multiple classes that we want to share.

How to create a mixin in JavaScript

Let's say we have a car and plane, and we want to calculate the maximum distance each of them can travel based on their fuel tank capacity and the average rate at which they consume their fuel.

Assume the maximum distance the car or plane can travel is given by:

maxDistance = tankCapacity/consumptionRate

Both a car and a plane are different in the way they consume their fuel and the inner workings of their engines, but one thing we know here is that we found a way to predict the maximum distance it can travel given the fuel tank capacity.

Code example

Let's look at the code below:

class Car {
constructor(tankCapacity, consumptionRate) {
this.tankCapacity = tankCapacity;
this.consumptionRate = consumptionRate;
}
// other car stuff
}
class Plane {
constructor(tankCapacity, consumptionRate) {
this.tankCapacity = tankCapacity;
this.consumptionRate = consumptionRate;
}
// other plane stuff
}
const ComputeMaxDistanceMixin = {
computeMaxDistance() {
// return absolute distance
return Math.round(this.tankCapacity / this.consumptionRate);
},
};
// creating a mixin 'extend' with Object.keys()
const extend = (obj, mixin) => {
Object.keys(mixin).forEach((key) => (obj[key] = mixin[key]));
};
// Could also implement 'extend' it like below
/* const extend = (obj, mixin) => {
Object.assign(obj.prototype, myMixin);
}; */
extend(Car.prototype, ComputeMaxDistanceMixin);
extend(Plane.prototype, ComputeMaxDistanceMixin);
const myCar = new Car(60, 1.2);
const myPlane = new Plane(5321, 2.7);
console.log(myCar.computeMaxDistance());
console.log(myPlane.computeMaxDistance());

Code explanation

  • Lines 1 to 8: We create our Car class. Our car will hold information about it's tankCapacity and consumptionRate.
  • Lines 10 to 17: We also hold the definition of our Planeclass. It is similar to our Car class.

The method used to compute the maximum distance traveled is the same for each, so we violate DRY (don’t repeat yourself) if we implement the method in each class separately. To solve this, we can create a mixin called ComputeMaxDistanceMixin which we'll use to extend our Plane and Car class.

  • Line 19: We define our mixin called ComputeMaxDistanceMixin which has a method to compute the maximum distance our plane or car can travel based on their tank capacity and consumption rate.
  • Line 20: We define our computeMaxDistance() method which returns the ratio of tankCapacity and consumptionRate of our vehicle.

Because this method is not part of our class definitions, we need a way to insert this into our class while avoiding repetition dynamically.

Fortunately for us, it is possible to modify the prototype of our class to include functions from our mixin.

  • Lines 27 to 29: We create our extend function, which helps extend our class's prototype to include the computeMaxDistance function. Our Mixin is an object, so we do this by iterating through the keys of our mixin and setting the key-value pairs in our class prototype.
  • Lines 32 to 34: We have a similar implementation of the extend function but with Object.assign() . This assigns all the key-value pairs from our mixin to our class prototype. We can use any of the implementations.
  • Lines 36 and 37: We extend our Car and Plane prototype with the mixin.
  • Lines 39 and 40: We create an instance of our Car and Plane , respectively.
  • Lines 42 and 43: We test if our mixin actually works by computing the maximum distance of each of our vehicles. Then we print it in our terminal.

Once we have extended them, both objects of Plane and Car can run computeMaxDistance().

This method allows us to construct complex behaviors in our code more easily than otherwise.