Tutorial

Using JavaScript Mixins

Published on February 12, 2019
Default avatar

By Vijay Prasanna

Using JavaScript Mixins

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

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.

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.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Vijay Prasanna

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel