Orientação a Objetos em Javascript – Função Construtora
Javascript é uma linguagem muito flexível.
E apesar de muitos desenvolvedores não terem conhecimento, javascript é muito orientada a objetos. Tanto que só existem 5 tipos primitivos que não são objetos na linguagem: numérico, string, booleano, null e undefined. Sendo que os 3 primeiros ainda podem ser convertidos em objetos pela própria linguagem internamente, ou por nós mesmos.
Eu já mostrei como é a Orientação a Objetos em Javascript, usando objetos literais. Particularmente, esse é estilo que eu prefiro. Mas nem por isso é o único correto. Existem outras formas de se programar com os conceitos de objetos em javascript.
Sem Classes
Vale a pena repetir: javascript não possui classes. E não possui por que não precisa, tanto que a ECMA4 foi abandonada por isso. Ao pensar em javascript, pense diretamente em objetos (afinal, quase tudo já o é).
Funções Construtoras
Você pode criar objetos usando funções construtoras:
var tt = new Tooltip();
tt.init();
Uma maneira de chegar a este código em javascript, é definindo a nossa função construtora:
function Tooltip() {
this.init = function() {
//..
}
}
O problema disso, é que sempre que instanciarmos um novo objeto, a função init será novamente criada na memória, e nem sempre isso é bom ao nosso projeto.
var tt = new Tooltip();//aloca o objeto tt, criando a função init
var tt2 = new Tooltip();//aloca o objeto tt2, criando novamente init para este objeto agora```
## .prototype
Se adicionarmos o método init ao prototipo do objeto, todos as instâncias herdarão esse método e não estaremos enchendo a memória com declarações repetidas.
``` js
function Tooltip() {}
Tooltip.prototype.init = function() {}
No github: https://github.com/wbruno/examples/tree/gh-pages/tt eu coloquei algumas formas diferentes de programar javascript.
Esse tooltip funciona basicamente só com css, através do pseudo seletor :hover. O papel do javascript, é procurar o conteúdo do tooltip apartir do atributo data-rel, e reescrever a marcação html do elemento que disparou o tooltip.
var Tooltip =(function(){
"use strict";
function Tooltip() {}
Tooltip.prototype.init = function(objs) {
var i = objs.length;
while(i--) {
var obj = objs[i],
wrap = this.createWrap(obj),
ttContent = this.getContent(obj);
wrap.appendChild(ttContent);
}
return objs;
};
Tooltip.prototype.createWrap = function(obj) {
var wrap = document.createElement('span');
wrap.className = 'tt-wrap';
obj.parentNode.replaceChild(wrap, obj);
wrap.appendChild(obj);
return wrap;
};
Tooltip.prototype.getContent = function(obj) {
var rel = obj.getAttribute('data-rel');
return document.querySelector(rel);
};
return Tooltip;
}());
var $ = function(selector) {
return document.querySelectorAll(selector);
};
window.addEventListener("load", function() {
var tt = new Tooltip();
tt.init($('.tt'));
tt.init($('#another'));
});
Visibilidade
Também usei o pattern de revelação nesta versão do código, mas diferente do código com objeto literal, onde apenas a função tt.init(), estava acessível ao mundo externo, aqui, todo o objeto Tooltip é público, através das suas instâncias.
for(var prop in tt) {
console.log(prop);//init, createWrap, getContent
}
console.log(tt.init);//function()
console.log(tt.createWrap);//function()
console.log(tt.getContent);//function()
Instâncias
Quando adicionamos um método ao prototype de um objeto todas as instâncias herdarão esse método. Mas o objeto em si não. Veja a saída:
console.log(Tooltip.init);//undefined
console.log(Tooltip.prototype.init);//function()
console.log(tt.init);//function()
Isso por que criamos inicialmente um objeto Tooltip vazio com uma função construtora .(funções também são objetos em javascript).
E, se não tivéssemos usado o prototype:
var Tooltip =(function(){
"use strict";
function Tooltip() {}
Tooltip.init = function(objs) {
//..
};
Tooltip.createWrap = function(obj) {
//..
};
Tooltip.getContent = function(obj) {
//..
};
return Tooltip;
}());
var $ = function(selector) {
return document.querySelectorAll(selector);
};
window.addEventListener("load", function() {
Tooltip.init($('.tt'));
Tooltip.init($('#another'));
var tt = new Tooltip();
console.log(tt.init);//undefined
console.log(Tooltip.init);//function()
});
As funções existiriam somente no objeto Tooltip, e não nas instâncias dele. A menos que copiássemos explicitamente os métodos de Tooltip, para dentro da instância tt:
tt.init = Tooltip.init;
console.log(tt.init);//function()
Nenhuma é mais ou menos correta
Não é mais correto ou menos errado. Funções construtoras ou objetos literais, cada forma de programar implica em estilos diferentes de codificar e resolve problemas, assim como alguns patterns só são implementados em um estilo e não no outro.
Fique a vontade para escolher qual forma mais lhe agrada, ou esteja pronto a continuar uma forma já definida pela sua equipe. O importante é ter em mente que existem diferentes formas de fazer uma mesma coisa, mas não mais ou menos correta por isso.
Demonstração
Subi no github a demonstração de todos as formas de fazer esse tooltip