Lesson 64 of 80
JavaScript classes only get one parent. Mixins are how you bolt on extra behavior — serializable, observable, whatever — without a rigid inheritance tree.
14 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A JS class can extends exactly one other class. There's no extends A, B like some languages allow. But real objects often need behavior from more than one source — a User that's both serializable and observable, say. A mixin is a function that takes a class and returns a new class with extra methods bolted on. You compose behavior by calling functions, not by fighting a single-parent hierarchy.
The mechanism is nothing exotic: a mixin is just a function (Base) => class extends Base { ... }. Since a function can return a new class each time, you can chain as many as you want.
const Serializable = (Base) =>
class extends Base {
toJSON() {
return { ...this };
}
};
const Observable = (Base) =>
class extends Base {
constructor(...args) {
super(...args);
this._listeners = [];
}
on(fn) {
this._listeners.push(fn);
}
emit(event) {
this._listeners.forEach((fn) => fn(event));
}
};
class Base {}
class User extends Observable(Serializable(Base)) {
constructor(name) {
super();
this.name = name;
}
}
const u = new User("Adesh");
u.on((e) => console.log("changed:", e));
u.emit("name-updated");
console.log(JSON.stringify(u)); // {"name":"Adesh","_listeners":[]}
Observable(Serializable(Base)) reads inside-out: Base gets Serializable's methods first, then that result gets Observable's methods layered on top. User extends the final, combined class. Each mixin only knows about Base (via super) — it doesn't know or care what else got mixed in before it, which is what makes them composable in any order (mostly — see below).
mixin keyword. If a function takes a class and returns class extends thatClass { ... }, it's a mixin — recognizing the shape matters more than memorizing a name.Observable(Serializable(Base)) and Serializable(Observable(Base)) behave differently if both mixins override the same method, because each one's super call points at whatever came before it in the chain. Don't assume mixins commute.Mixin(Base) is a genuine intermediate class, so instanceof checks against a mixin's returned class will actually pass, and you can end up with a deep chain if you're not careful.A mixin's magic isn't magic — it's the same prototype chain that ordinary class extends builds, just constructed by a function call instead of a static declaration. If prototype delegation already makes sense to you, a mixin is just "build one more link in the chain, programmatically, as many times as you want."