Wednesday, 13 February, 2019 UTC


Summary

When it comes to advanced class composition, JavaScript has quite a number of approaches - a true smorgasbord of options. A type of pattern that is rarely spotted in the wild is the mixin-based inheritance pattern. Mixins are usually skipped by new JavaScript programmers (I did it too). I don’t want to complain but mixins can sometimes be quite dense to write and comprehend. But they come with a bunch of features that are worth looking into.
The mixin pattern - as the name suggests - is a pattern of mixing together an object with other objects to add properties we need. Think of it like add-ons that can give your object additional properties, but these individual properties are not really subclasses themselves.

Educative.io: Learn by Coding ⤵

Build real apps. No experience required. Check out “The Complete JS Course”
ⓘ About this sponsored link
On the surface, mixins behave like object mixing layers, where we pass-in the target (the mixin) and the source. The target is appended to the source and a new object is returned.
A more accurate description is that a mixin works as factory where new a subclass object is returned. Through this whole process there is no definition of the subclass anywhere.
A more C++ analogy would be to compare them to abstract classes with virtual functions, allowing them to be inherited by other subclasses.
So, now that we know mixins allow us to create a modified definition that can be applied to existing superclasses to create new subclasses, let’s see how mixin would look like:
//The swim property here is the mixin let swim = { location() { console.log(`Heading ${this.direction} at ${this.speed}`); } }; let Alligator = function(speed, direction) { this.speed = speed, this.direction = direction }; //This is our source object let alligator = new Alligator('20 mph','North'); alligator = Object.assign(alligator, swim); console.log(alligator.location());
In the above snippet, we want to create an alligator that can swim. So we create a new alligator and then give it the swim feature. The swim object is the mixin or an extension that we want the alligator object to have using the Object.assign method.
The Object.assign method allows us to add more than one mixin at a time. A multiple mixin case would look like this:
alligator = Object.assign(alligator, swim, crawl);
Now let’s look how mixins can be used with ES6 classes:
let swim = { setSwimProperties(speed, direction) { this.speed = speed; this.direction = direction; }, getSwimProperties(){ console.log(`swimming ${this.speed} towards ${this.direction}`); } } class Reptile { constructor(name) { this.name = name; } } Object.assign(Reptile.prototype, swim); let alligator = new Reptile("alligator"); alligator.setSwimProperties("5 m/s", "upstream"); alligator.getSwimProperties();
The advantage of adding functionality via the mixin approach is flexibility. The mixin being a very primitive function, as in it does exactly one thing, allows us to use these structures repeatedly and in a variety of scenarios. It can used with a native function call, used in a class definition, etc.
Another good thing is that it tends to keep the class hierarchy horizontal - by allowing superclasses to use the mixins to create new objects of desired subclass properties rather than making the inheritance chain longer for creating new sublasses for these cases.
Some things to keep in mind when using mixins though:
  • The Object.assign (both in object and class implementation) does only a shallow copy of the mixin properties.
  • There can be potential name clashes while using properties from different mixins (the diamond problem in multiple inheritance)
  • It can be quite difficult to figure out from which mixin the property came from, since the properties are copied onto the source object. The instanceof operator cannot help us here.