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 distancereturn 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
Carclass. Our car will hold information about it'stankCapacityandconsumptionRate.
- Lines 10 to 17: We also hold the definition of our
Planeclass. It is similar to ourCarclass.
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
ComputeMaxDistanceMixinwhich 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 oftankCapacityandconsumptionRateof 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
extendfunction, which helps extend our class's prototype to include thecomputeMaxDistancefunction. 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
extendfunction but withObject.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
CarandPlaneprototype with the mixin. - Lines 39 and 40: We create an instance of our
CarandPlane, 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.