Rationale
ECMAScript 6 has classes and inheritance (via extends
). It should also
have mixins (that I like). Now it does.
Installation
Local installation
npm install --save sirrobert-mixin
Global installation
npm install --global sirrobert-mixin
Usage
Writing a Good Mixin
The key to writing a good mixin is to make it a composable function. The idea is that the mixin definition is actually a factory rather than a function itself.
Mixins are Factories (not functions)
Here's the pattern:
let MyMixin = (superclass) => class extends superclass {
constructor (params) {
super(params);
// ... and other stuff
}
}
When you use the Mixin.with()
method (or class method), you are passing in
the superclass and it does a little magic inside to create a new inheritance
chain. In particular, it looks like this:
// Class definition
class B extends Mixin(A).with(X,Y) { ... }
// Inheritance chain
B > X > Y > A
If you want a chain more like this:
// Mixins are after A
B > A > X > Y
You would use this:
class A extends Mixin.with(X, Y) {}
class B extends A {}
Building some Classes
Let's build some classes and see the inheritance chain in action. First, we load the module and define a base class and some mixins.
// I like to use "Mix" because it's a short verb.
const Mix = require("sirrobert-mixin");
class A {
constructor () {
this.foo = "Foo from A";
this.a = "only in A";
}
}
let Mixin1 = (superclass) => class extends superclass {
constructor (params) {
super(params);
this.foo = "Foo from Mixin1";
this.mixin1 = "only in mixin1";
}
}
let Mixin2 = (superclass) => class extends superclass {
constructor (params) {
super(params);
this.foo = "Foo from Mixin2";
this.mixin2 = "only in mixin2";
}
}
// This one doesn't have a .foo property so we can see the transparency.
let MixinNoFoo = (superclass) => class extends superclass {
constructor (params) {
super(params);
this.mixinNoFoo = "only in mixinNoFoo";
}
}
Now let's see how we can mix and match these into new classes.
Create a class that uses one mixin.
class MyClass extends Mix.with(Mixin1) {
constructor (params) {
super(params);
this.myClass = "only in myClass";
}
}
console.log(new MyClass().myClass); // "only in myClass"
console.log(new MyClass().foo); // "Foo from Mixin1"
Create a class with multiple mixins
With multiple mixins, the earlier mixins in the list override the latter.
class MyClass extends Mix.with(Mixin1, Mixin2) {
constructor (params) {
super(params);
this.myClass = "only in myClass";
}
}
console.log(new MyClass().myClass); // "only in myClass"
console.log(new MyClass().foo); // "Foo from Mixin1"
console.log(new MyClass().mixin1); // "only in Mixin1"
console.log(new MyClass().mixin2); // "only in Mixin2"
Create a class with superclass and mixins
When extending another class, you can insert mixins into the chain.
class MyClass extends Mix(A).with(Mixin1, Mixin2) {
constructor (params) {
super(params);
this.myClass = "only in myClass";
}
}
console.log(new MyClass().myClass); // "only in myClass"
console.log(new MyClass().foo); // "Foo from Mixin1"
console.log(new MyClass().mixin1); // "only in Mixin1"
console.log(new MyClass().mixin2); // "only in Mixin2"
console.log(new MyClass().a); // "only in A"
Sugar
The .with()
method is available as an instance method and a class method.
This allows you to add mixins when you have a base class, but also when you
just want the mixins. These are equivalent, but I think the second looks
neater, having less syntactic noise.
class B extends Mix().with(X,Y) {}
class B extends Mix.with(X,Y) {}