Mais uma vez vi uma dúvida, do mesmo usuário, no Stack Overflow em Português, com a seguinte pergunta:
O que são “mixins” [em JavaScript]?
Vejo em alguns códigos JavaScript e gostaria de saber o que é; exemplos reais de uso.
Por ter achado interessantíssima a pergunta e algo realmente relevante, decidi então elaborar uma resposta. Pois bem, aos interessados, ela segue abaixo.
Mixin em JavaScript é um uma classe pensada com foco no DRY. Ela dá a habilidade do desenvolvedor pegar atalhos para resolver problemas, quase como os famosos “helpers”, com a diferença de que um mixin pode ou não referenciar ou ser referenciado por um módulo1 sem a necessidade direta de extensão ou herança.
Um exemplo prático e útil
Botões (<button>) e âncoras (<a>) são conceitualmente componentes do DOM, o que significa que quando/se falarmos de “componentes”, estamos indo na síntese do negócio; indo na camada mais genérica dos elementos que a nossa linguagem favorita de marcação trabalha.
Num todo, componentes são materiais renderizáveis; são itens que quando marcados no seu HTML têm a capacidade de se invocar para a visualização do usuário e materialização da interface.
Portanto, exemplifiquei mixin nesse cenário, através de JavaScript. Usando botões e âncoras ainda, desenvolvi os seguintes objetos:
var Button = { design: { colors: { background: 'blue', text: 'yellow' }, borderRadius: 3, padding: 5, }, shape: function () { return '<button style="background-color:' + this.design.colors.background + ';' + 'border-radius:' + this.design.borderRadius + 'px;' + 'padding:' + this.design.padding + 'px;' + 'color:' + this.design.colors.text + ';' + 'border: none;">' + this.getContent() + '</button>'; } };
e
var Anchor = { design: { color: 'green', underline: true }, destination: 'google.com', shape: function () { return '<a style="color:' + this.design.color + ';' + 'text-decoration:' + (this.design.underline ? 'underline' : 'none') + ';" ' + 'href="http://' + this.destination + '">' + this.getContent() + '</a>'; } };
Eles possuem características em comum, certo? São exemplos design e shape. Entretanto, se você prestar atenção, são diferenciados pela sua implementação; pela suas características particulares. Em outras palavras, cada um desses “componentes” possui a sua própria personalidade, como a marcação em si, que no caso do botão é <button> e no caso da âncora é <a>.
Relembrando…
Lembra que há pouco falei sobre “renderização”? Pois é, componentes como esses devem ter a capacidade de se manifestarem no DOM. Então, faríamos para ensinar os dois objetos (Button e Anchor) a se projetarem sem cair na repetição de criar métodos iguais em seus escopos? Mixins!
Veja este terceiro objeto que desenhei:
var Component = { render: function (platform) { $(platform).html(this.shape()); }, append: function (platform) { $(platform).append(this.shape()); }, setContent: function (content) { this.content = content; return this; }, getContent: function () { return this.content; } };
Ele, por sua vez, possui recursos que podem tornar os nossos botões e âncoras úteis – e podemos usar livremente clonando o seu escopo entre os nossos dois objetos.
Para a clonagem, por sua vez, utilizei o método extend() do Underscore.js:
_.extend(Anchor, Component); _.extend(Button, Component);
O resultado ficou simples. Para renderizarmos os nossos componentes, faríamos então o seguinte:
Button .setContent('Register') .append('body'); Anchor .setContent('And here if you already have an account!') .append('body');
Se juntarmos tudo, o resultado será este:
var Component = { render: function (platform) { $(platform).html(this.shape()); }, append: function (platform) { $(platform).append(this.shape()); }, setContent: function (content) { this.content = content; return this; }, getContent: function () { return this.content; } }; var Button = { design: { colors: { background: 'blue', text: 'yellow' }, borderRadius: 3, padding: 5, }, shape: function () { return '<button style="background-color:' + this.design.colors.background + ';' + 'border-radius:' + this.design.borderRadius + 'px;' + 'padding:' + this.design.padding + 'px;' + 'color:' + this.design.colors.text + ';' + 'border: none;">' + this.getContent() + '</button>'; } }; var Anchor = { design: { color: 'green', underline: true }, destination: 'google.com', shape: function () { return '<a style="color:' + this.design.color + ';' + 'text-decoration:' + (this.design.underline ? 'underline' : 'none') + ';" ' + 'href="http://' + this.destination + '">' + this.getContent() + '</a>'; } }; _.extend(Anchor, Component); _.extend(Button, Component); Button .setContent('Register') .append('body'); Anchor .setContent('And here if you already have an account!') .append('body');
Para brincar e praticar, aqui está o jsFiddle.