Mixins

JavaScript gives every object exactly one [[Prototype]], and a class can extend exactly one other class. That single-parent rule is clean, but it bites the moment you want a class to be two things at once.

Say you have a Camera class and a Phone class, and you dream up a CameraPhone that is both. Inheritance can’t express that — you’d have to pick one parent and re-implement the other. Or take a more practical case: you have a Guest class, and separately an EventEmitter that knows how to broadcast events. You want Guest to gain that event-broadcasting skill without giving up whatever it already inherits.

The pattern that solves this is called a mixin.

Borrowing the Wikipedia definition: a mixin is a class holding methods that other classes can use without inheriting from it. Put differently, a mixin packages up a slice of behavior. You never instantiate it on its own — you stir it into other classes to give them that behavior.

Person
↓ extends
Guest
greetMixin
copied into →
Guest.prototype
Single inheritance flows down one chain; a mixin is stirred in sideways

A mixin example

The plainest way to build a mixin in JavaScript is to make an object full of useful methods, then merge those methods into the prototype of whatever class needs them.

Here greetMixin gives Guest the ability to speak:

// mixin
let greetMixin = {
  greet() {
    alert(`Welcome ${this.name}`);
  },
  farewell() {
    alert(`Goodbye ${this.name}`);
  }
};

// usage:
class Guest {
  constructor(name) {
    this.name = name;
  }
}

// copy the methods
Object.assign(Guest.prototype, greetMixin);

// now Guest can greet
new Guest("Maya").greet(); // Welcome Maya

No inheritance happens here at all. Object.assign walks the mixin’s own enumerable properties and copies them onto Guest.prototype, so afterward greet and farewell live directly on the prototype as if you’d written them there.

Try it below. The Guest class defines nothing but a constructor — every method the buttons call was copied in from the mixin. Change the name and watch this.name resolve on the instance:

interactiveStirring a mixin into a class
greetMixin
greet()
farewell()
Guest.prototype
constructor
greet() (copy)
farewell() (copy)
Object.assign copies the mixin's methods onto the class prototype

Because nothing was inherited, the [[Prototype]] slot stays free. That means Guest can extend a real parent class and pick up the mixin at the same time:

class Guest extends Person {
  // ...
}

Object.assign(Guest.prototype, greetMixin);

Guest inherits from Person through the prototype chain, and gains the mixin’s methods by direct copy. The two mechanisms don’t compete.

Mixins can use inheritance internally

A mixin is an object, and objects can have their own prototype. So a mixin can inherit from another mixin. Here greetMixin builds on top of speakMixin:

let speakMixin = {
  speak(phrase) {
    alert(phrase);
  }
};

let greetMixin = {
  __proto__: speakMixin, // (or we could use Object.setPrototypeOf to set the prototype here)

  greet() {
    // call parent method
    super.speak(`Welcome ${this.name}`); // (*)
  },
  farewell() {
    super.speak(`Goodbye ${this.name}`); // (*)
  }
};

class Guest {
  constructor(name) {
    this.name = name;
  }
}

// copy the methods
Object.assign(Guest.prototype, greetMixin);

// now Guest can greet
new Guest("Maya").greet(); // Welcome Maya

The interesting part is the super.speak(...) calls marked (*). You might expect super to reach into Guest’s parent — but Guest has no parent here. So where does super.speak find speak?

Look at the picture below, focusing on the right side:

Guest.prototype
greet()
farewell()
↑ [[Prototype]]
new Guest(“Maya”)
greetMixin
greet() [[HomeObject]]
farewell() [[HomeObject]]
↑ [[Prototype]]
speakMixin
speak(phrase)
super resolves through [[HomeObject]].[[Prototype]] — the mixin's own prototype, not the class's

The reason is [[HomeObject]]. When you write a method with the shorthand syntax (greet() { ... } inside an object literal), the engine permanently records where that method was born in a hidden [[HomeObject]] slot. For greet and farewell, that home is greetMixin, because that’s the literal they were written in.

Copying the methods with Object.assign doesn’t change [[HomeObject]]. The functions still remember greetMixin as home, even sitting on Guest.prototype.

And super.method always resolves as [[HomeObject]].[[Prototype]].method. So super.speak looks up greetMixin.[[Prototype]], which is speakMixin — and finds speak there. The lookup follows the mixin’s chain, not the class’s.

EventMixin

Let’s build a mixin you’d actually reach for. Many browser objects can generate events — a clean way to shout “something happened” to any code that cares, without those listeners knowing anything about each other. We’ll package that ability into a mixin so any class can gain it.

The mixin exposes three methods:

  • .trigger(name, [...data]) — fires an event. name identifies the event; anything after it is data passed along to listeners.
  • .on(name, handler) — registers handler to run whenever an event with that name fires. It receives the arguments from the matching .trigger call.
  • .off(name, handler) — unregisters that specific handler.

Once mixed in, a player object could trigger("play") when a song starts, and a separate scrobbler object could on("play", ...) to record what was heard. Or a playlist fires "play" when a track is chosen, and unrelated code reacts. The emitter and the listeners stay decoupled.

Here’s the implementation:

let eventMixin = {
  /**
   * Subscribe to event, usage:
   *  playlist.on('play', function(track) { ... }
  */
  on(eventName, handler) {
    if (!this._eventHandlers) this._eventHandlers = {};
    if (!this._eventHandlers[eventName]) {
      this._eventHandlers[eventName] = [];
    }
    this._eventHandlers[eventName].push(handler);
  },

  /**
   * Cancel the subscription, usage:
   *  playlist.off('play', handler)
   */
  off(eventName, handler) {
    let handlers = this._eventHandlers?.[eventName];
    if (!handlers) return;
    for (let i = 0; i < handlers.length; i++) {
      if (handlers[i] === handler) {
        handlers.splice(i--, 1);
      }
    }
  },

  /**
   * Generate an event with the given name and data
   *  this.trigger('play', data1, data2);
   */
  trigger(eventName, ...args) {
    if (!this._eventHandlers?.[eventName]) {
      return; // no handlers for that event name
    }

    // call the handlers
    this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
  }
};

Walking through the pieces:

  • .on(eventName, handler) — attaches handler to run when the named event occurs. The handlers live in an _eventHandlers property, an object mapping each event name to an array of functions. The first subscription lazily creates that store, so nothing is set up until it’s needed.
  • .off(eventName, handler) — finds handler in the list and splices it out. The i-- after the splice matters: removing an item shifts everything left, so the loop steps the index back to avoid skipping the next handler.
  • .trigger(eventName, ...args) — runs every handler registered for that name. Each is called with handler.apply(this, args), so inside a handler this is the emitting object and the arguments are whatever trigger was given after the name.
this._eventHandlers
“play”[ handlerA, handlerB ]
“ended”[ handlerC ]
_eventHandlers: a map from event name to a list of listener functions

Here’s the flow of a triggered event, end to end:

playlist.play(“Nocturne”)
this.trigger(“play”, “Nocturne”)
look up _eventHandlers[“play”]
handler(“Nocturne”) runs
playlist.play → trigger('play') → every registered handler runs with the data

Usage:

// Make a class
class Playlist {
  play(track) {
    this.trigger("play", track);
  }
}
// Add the mixin with event-related methods
Object.assign(Playlist.prototype, eventMixin);

let playlist = new Playlist();

// add a handler, to be called when a track plays:
playlist.on("play", track => alert(`Now playing: ${track}`));

// triggers the event => the handler above runs and shows:
// Now playing: Nocturne
playlist.play("Nocturne");

Playlist.play doesn’t know or care who’s listening — it just calls this.trigger("play", track). Any code that wants to react registers with playlist.on("play", ...). You can add that reaction from anywhere, and you can add as many classes’ worth of event behavior as you like, all without touching their inheritance chains.

The demo below wires up the exact eventMixin from above onto a Playlist. Choosing a track fires play; the two listeners react independently, and you can subscribe or unsubscribe each one with .on / .off while the playlist keeps working, oblivious to who is watching:

interactiveEventMixin: trigger, on, off

Summary

Mixin is a general object-oriented term: a class (or, in JavaScript, a plain object) that supplies methods for other classes to use.

Some languages allow a class to inherit from several parents. JavaScript does not — one [[Prototype]], one extends. Mixins fill that gap by copying methods into a prototype rather than inheriting them, so a class can pick up several independent behaviors on top of its single real parent. Event handling, as shown above, is a textbook use.

The one real hazard: copying methods can silently overwrite a method the class already had if the names collide. Name mixin methods deliberately, ideally with a recognizable prefix or a namespace, to keep the odds of a clash low.