Примесь (mixin) — это объект с набором функций, который сам по себе (отдельно от других объектов) не используется.
Вот, например, прекрасная примесь:
var Mixin_Babbler = { say: function () { console.log("My name is " + this.name + " and i think:'" + this.THOUGHTS + "'"); }, argue: function() { console.log("You're totally wrong"); } };
При попытке вызвать метод say просто так, нас ждет облом, потому что ни this.name, ни this.THOUGHTS в нашем объекте, почему-то, просто нет.
На самом деле все правильно, чтобы использовать примесь по назначению нам нужен другой объект, и метод, который копирует все свойства переданных ему объектов-примесей в прототип функции конструктора — обычно такой метод называют extend, или mix, или как-нибудь в этом духе:
function extend(object) { var mixins = Array.prototype.slice.call(arguments, 1); for (var i = 0; i < mixins.length; ++i) { for (var prop in mixins[i]) { if (typeof object.prototype[prop] === "undefined") { object.prototype[prop] = mixins[i][prop]; } } } }
Ну и как это использовать?
Легко и не напрягаясь — допустим, у нас есть парочка примесей:
var Mixin_Babbler = { say: function () { console.log("My name is " + this.name + " and i think:'" + this.THOUGHTS + "'"); }, argue: function() { console.log("You're totally wrong"); } }; var Mixin_BeverageLover = { drink: function () { console.log("* drinking " + this.FAVORITE_BEVERAGE + " *"); } };
И, кому-то, возможно, уже знакомая, эволюционная цепочка:
function Man(name) { this.name = name; } Man.prototype = { constructor: Man, THOUGHTS: "I like soccer" } extend(Man, Mixin_Babbler); function Gentleman(name) { this.name = name; } Gentleman.prototype = { constructor: Gentleman, THOUGHTS: "I like Earl Grey", FAVORITE_BEVERAGE: "Tea" } extend(Gentleman, Mixin_Babbler, Mixin_BeverageLover); function Programmer(name) { this.name = name; } Programmer.prototype = { constructor: Programmer, THOUGHTS: "MVC, MVVM, MVP *___* like it!", FAVORITE_BEVERAGE: "Cofee", write_good_code: function () { console.log("*writing best code ever*"); this.drink(); } } extend(Programmer, Mixin_Babbler, Mixin_BeverageLover);
Каждый «класс» теперь не зависит от другого, а весь повторяющийся функционал реализован с помощью примесей.
Теперь стоит все проверить — вдруг заработает:
var man = new Man("Bob"); var gentleman = new Gentleman("Bill"); var programmer = new Programmer("Benjamin"); man.say(); man.argue(); gentleman.say(); gentleman.drink(); programmer.say(); programmer.write_good_code();
И консоль говорит что таки да, все как надо:
My name is Bob and i think:'I like soccer' *You're totally wrong* My name is Bill and i think:'I like Earl Grey' *drinking Tea* My name is Benjamin and i think:'MVC, MVVM, MVP like *__* it!' *writing best code ever* *drinking Cofee*
Собственно все. В отличие от «классического» наследования, реализация примесей очень простая и понятная. Существуют конечно некоторые вариации, но так или иначе ядро выглядит именно так.