mixomatic

Create mixins which work with instanceof (friendly for unit tests).

Usage no npm install needed!

<script type="module">
  import mixomatic from 'https://cdn.skypack.dev/mixomatic';
</script>

README

mixomatic

Create mixins which work with instanceof (friendly for unit tests). Internally references are handled by a WeakSet instances so there's no need to manually keep records of which objects have been mixed onto and risk memory leaks.

Install

With npm:

npm install --save mixomatic

With yarn:

yarn add mixomatic

Or alternatively, in a browser or deno you can use it directly in a page via unpkg as a module (not recommended for production use):

import mixomatic from 'https://unpkg.com/mixomatic';

Usage

Make a new mixin which appends propertyDescriptors to an object.

import mixomatic from 'mixomatic';

const myMixin = mixomatic(propertyDescriptors);

Mix onto an object.

const obj = {};

myMixin(obj);

Check if an object has been modified by a given mixin:

obj instanceof myMixin; // true

Also works with classes!

class MyClass {}

myMixin(MyClass.prototype);

const obj = new MyClass();

obj instanceof MyClass; // true
obj instanceof myMixin; // true

And inheritance!

class MyChildClass extends MyClass {}

const obj = new MyChildClass();

obj instanceof MyChildClass; // true
obj instanceof MyClass;      // true
obj instanceof myMixin;      // true

Example

You're making a game with a little ship which shoots space-bound rocks before they can bash into it. Both the ship and the rocks have position and velocity properties. You could make a class, which provides a move method, which they would both inherit from. However, that could be the beginning of a class hierarchy and you've heard bad things about those being hard to modify in the future. JavaScript also has no way to do multiple inheritance with classes, so your options are limited with classes anyway.

Instead you make the wise choice to use mixomatic! You use mixomatic to create a mixin called movable, which takes a time difference and uses it to update the position of its host object.

const movable = mixomatic({
  move: {
    value(dt) {
      this.position.x += dt * this.velocity.x;
      this.position.y += dt * this.velocity.y;
    },
    configurable: true,
    enumerable: false,
    writable: true
  }
});

Since there'll only be one ship, you define it directly as an object and apply movable to it to give it the move method.

const ship = {
  position: { x: 0, y: 0 },
  velocity: { x: 0, y: 0 }
};

movable(ship);

Asteroids are more numerous and can appear in all sorts of places, so you decide to go with a class for those.

class Asteroid {
  constructor(position, velocity) {
    this.position = { x: position.x, y: position.y };
    this.velocity = { x: velocity.x, y: velocity.y };
  }
}

movable(Asteroid.prototype);

Now both ship and Asteroid instances will have the move method, and will both appear to be instances of movable, yet are not part of the same class hierarchy. All sorts of behaviour can be written as mixins (for example, the ship can fire missiles, and so can UFOs).

This is useful because mixins can be tested in isolation, and you can avoid duplication of tests for mixed properties by using an instanceof check in the test suites of host objects like ship and Asteroid.