Mixins

November 18, 2018

In JavaScript we can only inherit from a single object. There can be only one [[Prototype]] for an object. And a class may extend only one other class.

To solve the use of multi inheritance we use mixins.

In object-oriented programming languages, a Mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes.

A mixin can also be viewed as an interface with implemented methods. This pattern is an example of enforcing the dependency inversion principle.

Inheriting “methods”

JavaScript does not have methods in the form that class-based languages define them. In JavaScript, any function can be added to an object in the form of a property. An inherited function acts just as any other property, including property shadowing

There is more than one type of prototypal inheritance:

Each type of prototypal inheritance has its own set of use-cases, but all of them are equally useful in their ability to enable composition, which creates has-a or uses-a or can-do relationships as opposed to the is-a relationship created with class inheritance.

Delegation

We can work with delegation by using apply, bind or call to inherit from another object. Let's see an example:

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

function Toy(name, price) {
  Product.call(this, name, price);
  this.category = 'toy';
}

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

This is another “tricky” example

Concatenative

Using Object.assign() as mixin

// mixin
let sayHiMixin = {
  sayHi() {
    alert(`Hello ${this.name}`);
  },
  sayBye() {
    alert(`Bye ${this.name}`);
  }
};

// usage:
const User = function(name) {
    this.name = name;
};

// copy the methods
Object.assign(User.prototype, sayHiMixin);
// now User can say hi
new User("Dude").sayHi(); // Hello Dude!

or if you were using classes you could do something like this:

class User extends Person {
  // ...
}

Object.assign(User.prototype, sayHiMixin);

###Functional Mixins

If the functions defined by mixins are intended solely for the use of other objects, why bother creating mixins as regular objects at all? Put another way, a mixin should be a process not an object.

The logical conclusion is to make our mixins into functions into which consumer objects inject themselves by delegation, thus cutting out the middle guy (the extend function) entirely.

var asCircle = function() {
  this.area = function() {
    return Math.PI * this.radius * this.radius;
  };
  this.grow = function() {
    this.radius++;
  };
  this.shrink = function() {
    this.radius--;
  };
  return this;
};
 
var Circle = function(radius) {
    this.radius = radius;
};

asCircle.call(Circle.prototype);

var circle1 = new Circle(5);
circle1.area(); //78.54

Mixins as verbs instead of nouns

Adding Options

This functional strategy also allows the borrowed behaviours to be parameterized by means of an options argument.

var asOval = function({growBy, shrinkBy}) {
  this.area = function() {/* ... */};
  this.ratio = function() {/* ...*/};
  this.grow = function() {
    this.shortRadius += (growBy/this.ratio());
    this.longRadius += growBy;
  };
  // ...
  return this;
}
 
var OvalButton = function(/*...*/) {
  //...
};
// ...
asOval.call(OvalButton.prototype, {growBy: 2, shrinkBy: 2});

Further reading Traits

Bibliography:
Angus Croll
javascript.info
Wikipedia
Eric Elliott
developer.mozilla

comments powered by Disqus