JavaScript Mixins & Composition
Build complex objects by composing simpler behaviors instead of using deep inheritance.
What is a Mixin?
A mixin is an object that provides methods you can add to other objects.
let Serializable = {
serialize() { return JSON.stringify(this); }
};
class User { constructor(name) { this.name = name; } }
Object.assign(User.prototype, Serializable);
let user = new User("John");
console.log(user.serialize()); // '{"name":"John"}'
Multiple Mixins
Combine multiple behaviors with Object.assign.
let Timestamped = {
setTimestamps() { this.createdAt = new Date(); }
};
let SoftDeletable = {
softDelete() { this.deletedAt = new Date(); },
get isDeleted() { return this.deletedAt !== null; }
};
class Post {
constructor(title) { this.title = title; this.setTimestamps(); }
}
Object.assign(Post.prototype, Timestamped, SoftDeletable);
let post = new Post("Hello");
post.softDelete();
console.log(post.isDeleted); // true
Composition Pattern
Build objects by combining behaviors at runtime.
let CanEat = {
eat(food) { console.log(`${this.name} eats ${food}`); return this; }
};
let CanMove = {
move(distance) { console.log(`${this.name} moves ${distance}m`); return this; }
};
function createOrganism(name) {
return Object.assign({ name }, CanEat, CanMove);
}
let dog = createOrganism("Dog");
dog.eat("bone").move(10);
Game Character Example
Common behaviors shared, type-specific abilities added conditionally.
let CanAttack = {
attack(target) {
let damage = Math.floor(Math.random() * 10) + 1;
console.log(`${this.name} attacks for ${damage} damage`);
return this;
}
};
let CanHeal = {
heal(amount) {
let healed = Math.min(amount, this.maxHealth - this.health);
this.health += healed;
return this;
}
};
function createCharacter(name, type) {
let character = { name, type, health: 100, maxHealth: 100 };
Object.assign(character, CanAttack);
if (type === "healer") Object.assign(character, CanHeal);
return character;
}
When to Use Composition
Use composition when behaviors are orthogonal and can be mixed. Use inheritance for clear “is-a” relationships with shared implementation.
Best Practices
- Favor composition over inheritance — More flexible
- Keep mixins focused — One responsibility each
- Return
this— Enable method chaining - Document dependencies — What mixins expect from the host
Common Mistakes
- Name collisions — Later mixins overwrite earlier ones
- Overcomplicating — Too many mixins become hard to trace
- Breaking encapsulation — Exposing internal state
- Using inheritance when composition fits — Prefer composition for orthogonal behaviors