Mixins for ES6 classes, transpiled with babel

Subclass Factory Mixins

There is another way to realize mixins in Javascript: With subclass factories.

A subclass factory is a function that excepts a base class and returns an extended subclass of this base class:

const mixin = base => class extends base {
  /* properties to mix in */
}

Subclass factories are possible for two reasons:

  • classes can be defined as expressions and are first class* in Javascript
  • extends clauses can contain arbitrary expressions

Let’s apply this pattern:

// superclass
class CartoonCharacter {
  constructor(author) { this.author = author }
  drawnBy() { return "drawn by " + this.author }
}

// mixin 1
const Human = base => class extends base {
  haveFun() { return "drinking beer" }
}

// mixin 2
const Simpson = base => class extends base {}

// composed subclass
const Homer = Simpson(Human(CartoonCharacter));

// create an instance
const homer = new Homer("Matt Groening")

console.log(homer.drawnBy());
console.log(homer.haveFun());

What are the advantages of mixins by sublcass factories?

  • natural property precedence: Later mixed in properties override prior properties of the same name
  • both mixins (Human/Simpson) and subclasses (Homer) can use super as usual
  • mixins can have constructors (stateful mixins)
  • no mutation of prototypes or instances

Stateful Mixins

State makes things hard. You should avoid state, whenever possible. However, sometimes your mixin needs its own state, which is passed through a constructor:

class CartoonCharacter {
  constructor(author) { this.author = author }
  drawnBy() { return "drawn by " + this.author }
}

const Human = base => class extends base {
  haveFun() { return "drinking beer" }
}

const Simpson = base => class extends base {
  constructor(arg, ...superArgs) {
    super(...superArgs);
    this.name = arg;
  }
  sayHey() { return "Hey, I am " + this.name }
}

const Homer = Simpson(Human(CartoonCharacter));
const homer = new Homer("Homer Simpson", "Matt Groening")

console.log(homer.drawnBy());
console.log(homer.haveFun());
console.log(homer.sayHey());

*the term first class functions means that functions can be passed around as arguments or return values like normal data

Leave a Comment