Перейти к содержимому

Mixins (композиция)

В одной фразе: миксины — паттерн горизонтального переиспользования поведения, обходящий ограничение одиночного наследования в JS. На собесе ждут несколько реализаций (Object.assign vs subclass factory), знание проблемы diamond problem и понимание порядка применения.

// Mixin через Object.assign: простой, но методы становятся enumerable
const Serializable = {
serialize() { return JSON.stringify(this); },
deserialize(s) { return Object.assign(Object.create(this), JSON.parse(s)); }
};
const Validatable = {
validate() { return Object.values(this).every(v => v !== null); }
};
class User {
constructor(name, email) { this.name = name; this.email = email; }
}
Object.assign(User.prototype, Serializable, Validatable);
const u = new User('Alice', 'a@b.com');
u.validate(); // true
u.serialize(); // '{"name":"Alice","email":"a@b.com"}'
// Subclass factory mixin — поддерживает super, private, правильную цепочку
const Timestamped = (Base) => class extends Base {
constructor(...args) {
super(...args);
this.createdAt = new Date();
}
};
const Tagged = (Base) => class extends Base {
#tags = new Set();
addTag(t) { this.#tags.add(t); return this; }
hasTag(t) { return this.#tags.has(t); }
getTags() { return [...this.#tags]; }
};
class Article { constructor(title) { this.title = title; } }
const TimestampedTaggedArticle = Timestamped(Tagged(Article));
const post = new TimestampedTaggedArticle('Hello World');
post.addTag('js').addTag('ts');
post.getTags(); // ['js', 'ts']
post.createdAt; // Date объект
// Diamond problem: порядок миксинов определяет, чей метод "победит"
const A = (Base) => class extends Base { greet() { return 'A'; } };
const B = (Base) => class extends Base { greet() { return 'B'; } };
class C extends A(B(Object)) {}
// Цепочка: C → anon(A) → anon(B) → Object
new C().greet(); // 'A' — A применён последним (внешний слой), идёт первым в цепочке
// Если нужен B:
class D extends B(A(Object)) {}
new D().greet(); // 'B'

Итог: Subclass factory mixins предпочтительнее Object.assign — они поддерживают super, private поля и правильную instanceof-цепочку.