Maio, 2009 – Aaron Newton of Clientcide
Tradução e Adaptação Fabio Zendhi Nagao
Atualmente, a maioria das pessoas começando a trabalhar com
JavaScript se deparam com a difícil tarefa de escolher uma biblioteca
ou pelo menos qual delas aprender primeiro. Se você estiver trabalhando
para uma empresa, é bem provável que ela já tenha escolhido uma framework para você, de modo que esta
escolha talvez seja discutível. Neste caso, se eles escolheram a MooTools e você está acostumado com a jQuery,
então talvez este artigo ainda possa ser interessante para você.
Todo dia
no twitter vejo várias mensagens que resumem a discussão
acima para “MooTools ou jQuery?”. O objetivo deste artigo é ajudá-lo a fazer esta escolha.
Sobre o autor
Eu sou um desenvolvedor da MooTools, trabalho com a MooTools framework, “blogo” sobre MooTools, escrevi o principal tutorial online
e o livro sobre MooTools.
Obviamente tenho a perspectiva de algum modo enviesada. Saliento também
que não uso muito a jQuery. Se você for um desenvolvedor da jQuery e
encontrar alguma má interpretação da minha parte, favor entrar em
contato para me ajudar a retificar o problema. Meu objetivo aqui é ser
útil e correto para as pessoas – não vender uma framework
sobre a outra.
Sobre o tradutor
Provalvemente um dos usuários mais antigos de MooTools do Brasil. Trabalhou na otimização do sistema de animação da framework, criou vários widgets (iCarousel,
fValidator, iMask, iFisheye – a maioria
deles já melhorada pela comunidade ou migrada para outras bibliotecas), trabalha profissionalmente com a MooTools tanto em client-side como em server-side.
Objetivo
Ajudá-lo a fazer a escolha entre essas duas frameworks envolve explicar como elas são diferentes. Vou começar dizendo que ambas são excelentes opções. Você não
irá fazer uma má escolha aqui. Ambas as frameworks possuem suas forças e fraquezas mas, em geral, elas são ótimas escolhas. Existem também outras frameworks dignas
de atenção: Dojo, Prototype, YUI, Ext
e outras são todas ótimas opções. A escolha de qualquer uma delas está
mais relacionada com o seu estilo do que com o que você precisa
realizar. Este artigo é focado em MooTools e jQuery, pois, cada vez
mais, são as duas frameworks que vejo as pessoas considerando. Finalmente, não estou tentando convencer ninguém a trocar uma framework
pela outra. Existem coisas interessantes em ambas sobre as quais você
pode aprender. Saiba um mais da motivação que me levou a escrever este
artigo no artigo do meu blog Clientcide.
Índice
- Os lemas dizem tudo
- A curva de aprendizado e a comunidade
- Para que JavaScript é bom
- Mais que simplesmente o DOM
- Herança com JavaScript
- Auto referência
- MooTools faz a JavaScript mais divertida
- jQuery faz o DOM mais divertido
- Qualquer coisa que você fizer, posso fazer melhor
- MooTools permite que você faça da sua própria maneira
- Encadeamento como um Design pattern
- Reutilizando código com a jQuery
- Reutilizando código com a MooTools
- MooTools e herança
- Extendendo e implementando classes
- A hora da decisão
- Discussão
Estatísticas
Núcleo da jQuery | Núcleo da MooTools | |
---|---|---|
Tamanho | 55.9K | 64.3K |
Recursos | ||
Licença | MIT & GPL | MIT |
Utilitários para DOM | sim | sim |
Animações | sim | sim |
Manipulação de eventos | sim | sim |
Seletores CSS3 | sim (um subgrupo) | sim (um subgrupo) |
AJAX | sim | sim |
Extensões nativas (excluindo Element) | aproximadamente uma dúzia para Array, Object, e String | aproximadamente seis dúzias para Array, Object, String, Function, e Number |
Herança | não suportada diretamente através de jQuery | oferecida pelo construtor Class |
Outras considerações | ||
plug-ins | centenas de plug-ins não oficiais em um diretório no endereço plug-ins.jquery.com | aproximadamente quatro dúzias de plug-ins oficiais disponíveis em mootools.net/more. Muitos outros pela web. Nenhum catálogo consolidado. |
Biblioteca oficial de UI | sim | não |
Dados baseados em informações retiradas de jquery.com, mootools.net e wikipedia.org.
Os lemas dizem tudo
Se você for para o site da jQuery, aqui está o que ele diz no topo da página sobre do que se trata a jQuery:
“jQuery is a fast andconcise JavaScript Library that simplifies HTML document traversing,event handling, animating, and Ajax interactions for rapid webdevelopment. jQuery is designed to change the way that you writeJavaScript.”
que podemos traduzir:
jQuery é uma bibliotecaJavaScript rápida e concisa que simplifica a manipulação do documentoHTML, eventos, animações e interação AJAX para o desenvolvimento webágil. jQuery é desenhado para mudar a forma que você escreve JavaScript.
… e se você for para a MooTools, isto é o que você vai encontrar:
MooTools is a compact,modular, Object-Oriented JavaScript framework designed for theintermediate to advanced JavaScript developer. It allows you to writepowerful, flexible, and cross-browser code with its elegant, welldocumented, and coherent API.
que podemos traduzir:
MooTools é uma frameworkde JavaScript compacta, modular, orientada a objetos e desenhada paraos desenvolvedores JavaScript de nível intermediário para avançado. Elapermite a você escrever códigos poderosos, flexíveis e cross-browser com a sua elegante, bem documentada e coerente API.
Acho que isto realmente diz tudo. Se você me perguntar (e estou assumindo que você perguntou), a questão não é sobre qual framework é melhor ou pior.
É sobre “Qual dessas coisas você deseja fazer?”. Essas duas frameworks simplesmente não estão tentando fazer a mesma coisa. Elas se sobrepõem em funcionalidades
oferecidas, mas não estão tentando fazer as mesmas coisas.
A auto-definição da jQuery fala sobre HTML,
eventos, animações, AJAX e desenvolvimento web. A MooTools fala sobre
orientação a objetos e escrever códigos poderosos e flexíveis. jQuery
aspira “mudar a forma que você escreve JavaScript” enquanto a MooTools
é desenhada para os desenvolvedores JavaScript de nível intermediário
para avançado.
Faz parte dessa consideração a noção de uma framework e uma toolkit. MooTools é uma framework que tenta implementar JavaScript “da forma que ela deveria ser” (de acordo
com os autores da MooTools). O objetivo é implementar uma API que nos faça sentir a JavaScript e melhore tudo, não apenas o DOM. jQuery é uma toolkit
que oferece uma coleção de métodos fáceis de utilizar em um sistema
auto-contido, desenhado para fazer o DOM mais agradável. Acontece
apenas que o DOM é onde a maioria das pessoas focam seus esforços
quando estão escrevendo JavaScript; então, em vários casos, jQuery é
tudo que você precisa.
A maioria do código que você escreve quando utiliza
MooTools continua parecendo JavaScript. Se você não está interessado em
JavaScript como uma linguagem, então aprender MooTools lhe fará sentir
como um condenado. Se você estiver interessado em JavaScript e o que a
faz interessante, poderosa e expressiva; então, pessoalmente, acho que
MooTools é uma melhor escolha.
A curva de aprendizado e a comunidade
Primeiramente, jQuery é, de longe, mais fácil de
aprender. Ela possui um estilo quase coloquial que praticamente não
parece como programação. Se tudo que você quer é pegar alguma coisa
funcionando rapidamente sem aprender JavaScript, a jQuery é
provavelmente a melhor escolha. Não é que a MooTools não consiga
ajudá-lo a realizar as mesmas coisas, mas vou admitir que pode ser um
pouco mais difícil para novatos em JavaScript trabalhar com ela. Existe
também o fato de que há um bocado de recursos disponíveis por aí para
ajudar você a aprender a jQuery – pelo menos mais que sobre a MooTools.
Se você comparar a comunidade da jQuery (veja Discussion na página da jQuery) e a comunidade da MooTools (irc,
mailing list e o fórum não oficial) você rapidamente perceberá duas coisas:
- A comunidade da jQuery é de longe maior (atribuo isso principalmente à facilidade de aprender jQuery, mas também porque…)
- Eles promovem a biblioteca mais ativamente.
Se você medir a jQuery e a MooTools sob métricas como: pessoas
utilizando, buscas realizadas no Google, número de livros vendidos,
etc… você verá que jQuery está à frente por uma grande margem de
folga.
Para explicar o porquê que você deve considerar MooTools, precisarei primeiro falar um pouco sobre o que ambas as frameworks fazem. Em última análise, a framework
que você escolher depende do que você pretende realizar e como você
gosta de programar (talvez até mesmo se você gosta de programar, pelo
menos em JavaScript).
Para quê JavaScript é bom
Parte de fazer esta escolha é perguntar o que
você quer fazer com JavaScript. Vamos considerar a JavaScript de
baunilha. Nenhuma framework; apenas JavaScript velha e
pura. JavaScript lhe oferece objetos nativos como Strings, Numbers,
Functions, Arrays,
Dates, Regular Expressions entre outros. JavaScript oferece também um modelo de herança – o de alguma forma esotérico modelo chamado herança prototipada
(do qual vou falar um pouco mais adiante). Esses blocos de montar e o
conceito de herança são o pão e a manteiga de qualquer linguagem de
programação e eles não têm absolutamente nada a ver com os navegadores,
a web, CSS ou HTML. Você pode escrever qualquer coisa que queira em
JavaScript. Tic-tac-toe, xadrez, editores de foto, um servidor web,
qualquer coisa. Acontece apenas que 99% de todo código JavaScript pelo
mundo afora roda em navegadores e é por isso que pensamos dela como “A
linguagem de programação para navegadores”.
Entender a diferença entre que o navegador, o DOM, é onde vamos
utilizar JavaScript a maioria do tempo e que na verdade JavaScript é
uma linguagem de programação muito robusta e expressiva irá ajudá-lo a
compreender a diferença entre MooTools e jQuery.
Mais que simplesmente o DOM
Se você pensa nas tarefas que desejamos realizar em
JavaScript estritamente como “pegar as coisas em uma página e trabalhar
coisas com elas” então jQuery é provavelmente a melhor escolha. Ela se
excede no oferecimento de um sistema muito expressivo para descrever o
comportamento na página de uma forma que muitas vezes não parece como
programação. Você ainda pode utilizar o restante da JavaScript para
fazer o que você precisa fazer, mas se você estiver focado no DOM
(mudar propriedades CSS, animar coisas, procurar conteúdo através de
AJAX, etc) a maioria dessas coisas serão cobertas pela jQuery e não vão
parecer puro e velho código JavaScript. jQuery oferece ainda alguns
métodos que não estão relacionados com o DOM; por exemplo: ela oferece
um mecanismo para iterar Arrays – $.each(array, fn) – ou, outro exemplo, ela oferece um método para
remover espaços do começo e do final de um String – $.trim(str).
Mas não existem muitos métodos utilitários deste tipo disponíveis, o
que é bom porque, na maioria dos casos, se você está apenas pegando
coisas do DOM, iterando-as, alterando-as de alguma forma (incluindo
HTML, alterando estilos, adicionando escuta de eventos onclick, onmouseover , etc) você não precisará de muitas outras coisas.
Mas se você pensar na JavaScript sob seu escopo
completo, verá que a jQuery não se foca em outras coisas além do DOM.
Esta é uma das razões pelas quais ela é tão fácil de aprender, mas isso
também limita as formas que ela pode ajudar você a escrever JavaScript.
Ela não quer ser nada além de um sistema de programação sólido para o
DOM. Ela não se preocupa com herança, nem com utilidades básicas de
todos os tipos nativos da linguagem JavaScript, mas ela não precisa. Se você precisar
mexer com Strings, Dates, Regular Expressions, Arrays e Functions, você pode. Não é tarefa da jQuery ajudar você a fazer isso. JavaScript como uma linguagem
está lá aos seus pés. jQuery transforma o DOM no seu playground, mas o resto do JavaScript não pertence ao seu escopo.
É neste ponto que MooTools é completamente diferente.
Ao invés de se focar exclusivamente no DOM (apesar de, como veremos em
instantes, ela oferecer toda as funcionalidades que a jQuery oferece,
mas faz isso de uma forma muito diferente), MooTools assume como seu
escopo a linguagem inteira. Se jQuery faz do DOM o seu playground, o objetivo da MooTools é transformar JavaScript no seu playground, e esta é uma das razões pelas quais ela é mais difícil de aprender.
Herança com JavaScript
A linguagem de programação JavaScript tem algumas coisas muito impressionantes. Para iniciantes, ela é uma linguagem funcional,
o que significa que ela trata funções como objetos que podem ser
manipulados como variáveis, assim como qualquer outro objeto – String e
Number por exemplo. Ela foi desenvolvida com esse conceito em mente e
muitos dos métodos e padrões funcionam melhor nela quando você programa
desta forma. É a diferença entre:
for (var i = 0; i < myArray.length; i++) { /* do stuff */ }
e
myArray.forEach(function(item, index) { /* do stuff */ });
JavaScript possui um modelo de herança que não é único mas é, pelo menos, mais raro em
linguagens de programação. Ao invés de classes que são definidas e podem ser derivadas, ela utiliza métodos da herança prototipada.
Isso significa que os objetos herdam diretamente de outros objetos. Se
você procurar uma referência em um objeto que herda de outro objeto, a
linguagem vai procurar por essa propriedade no objeto filho e, se ela
não o encontrar, procurar no seu pai. É assim que um método funciona em
um Array []. Quando você escreve:
[1,2,3].forEach(function(item) { alert(item) }); //this alerts 1 then 2 then 3
o método forEach não é uma propriedade da array definida ([1,2,3]), é uma propriedade do protótipo Array de todos os possíveis array. Quando você faz uma referência a esse método,
a linguagem procura por ele na sua array e, como não encontra, ela procura no protótipo Array. Isso significa que o método forEach
não está alocado na memória para cada array; ele está na memória apenas
para o protótipo Array da qual descendem todas as arrays. Isso é
inacreditavelmente eficiente e ultimamente muito poderoso. (Nota: o
alias da MooTools para o método forEach é each).
Auto referência
JavaScript possui uma palavra reservada this. É difícil definir de forma sucinta o que this é mas, por definição, this
é o objeto ao qual o método pertence. Ele permite aos objetos se
referirem a eles mesmos dentro dos seus métodos de forma que não
haveria outros meios de se fazer. Isto se torna importante quando você
cria objetos filhos e tem várias instâncias desse objeto; de qual outra
forma um método de um objeto poderia referenciar a si mesmo? Quando a
cópia do método existe no pai, não no filho, a palavra this permite que essas instâncias se refiram ao seu próprio estado (veja aqui uma descrição muito mais completa sobre a palavra reservada this e outra da Mozilla).
A palavra this permite que objetos que descendam de
outros objetos se auto-referenciem, mas existem casos onde você deseja
referenciar alguma outra coisa através de this.
Isto chama-se binding, onde você especifica um this diferente para um método. O método each
de um Array permite que você especifique um objeto como segundo
parâmetro. Segue abaixo um exemplo onde você pode querer passar um this diferente do padrão:
var ninja = {
weapons: ['katana', 'throwing stars', 'exploding palm technique'],
log: function(message) {
console.log(message);
},
logInventory: function() {
this.weapons.each(function(weapon) {
//we want "this" to point to ninja...
this.log('this ninja can kill with its ' + weapon);
}, this); //so we pass "this" (which is ninja) to Array.each
}
};
ninja.logInventory();
//this ninja can kill with its katana
//this ninja can kill with its throwing stars
//this ninja can kill with its exploding palm technique
No exemplo acima, nós amarramos ninja (que é o this dentro de logInventory) para o método que passamos para o array de forma que podemos nos referir à propriedade
log do ninja. Se nós não fizéssemos isso, this seria window (dentro de um navegador).
Estes são apenas alguns exemplos do poder e da expressividade que a JavaScript tem a oferecer – herança, auto-referecia, binding
e eficientes propriedades prototipadas. A má notícia é que JavaScript
de baunilha não oferece este poderio de forma acessível, e é aqui que
MooTools entra. Ela torna esses tipos padrões fáceis e um tanto
agradáveis de utilizar. Você acaba construindo mais códigos abstratos
e, a longo prazo, isto é uma coisa boa – uma coisa poderosa. Aprender
como esses padrões são valiosos, e como utilizá-los de forma correta,
exige esforço, mas o lado bom é que o código que você cria é tanto
altamente reutilizável como fácil de manter. Vou falar sobre esses
temas em alguns minutos.
MooTools faz a JavaScript mais divertida
Porque MooTools se foca em fazer a API da JavaScript
mais estável e coerente, ela é menos focada em oferecer uma interface
que “muda a forma que você escreve JavaScript” e mais em fazer da
linguagem como um todo muito menos frustrante. MooTools tenta ser uma
extensão da linguagem JavaScript. MooTools tenta fazer a JavaScript da
forma que ela deveria ser. Uma parte significante do núcleo da
biblioteca é gasta aumentando-se Function, String, Array, Number,
Element e outros protótipos. Uma outra grande inclusão é uma função
chamada Class.
Agora, para muitas pessoas, Class aparenta estar tentando recriar um modelo de herança mais clássico como o encontrado em Java ou C++, mas este não é o caso.
O que a Class
faz é oferecer, para você e para mim, uma forma mais fácil de utilizar
e obter os benefícios do modelo de herança prototipada do JavaScript.
Vale lembrar que estes conceitos não são particulares da MooTools
(outras frameworks
oferecem funcionalidades semelhantes), mas ambos os conceitos não estão
presentes na jQuery. A jQuery não oferece um sistema de herança, nem
qualquer melhoria para os objetos nativos (Function, String, etc). Esta
não é uma deficiência da jQuery, uma vez que os seus autores poderiam
facilmente oferecer estas melhorias. Entretanto, eles desenharam a toolkit
com um objetivo diferente em mente. Onde a MooTools objetiva fazer a
JavaScript mais divertida, jQuery objetiva fazer o DOM mais divertido e
os seus desenvolvedores escolheram limitar seu escopo a essa tarefa.
jQuery faz o DOM mais divertido
E é por isso que jQuery é mais acessível. Ela não
exige que você aprenda JavaScript profundamente. Ela não joga você na
profundidade com herança prototipada, binding, this e protótipos nativos.
Quando você começa com a jQuery no tutorial oficial, isto é o primeiro exemplo de código jQuery que você vai encontrar:
window.onload = function() {
alert("welcome");
}
e aqui está o terceiro:
$(document).ready(function() {
$("a").click(function(event) {
alert("Thanks for visiting!");
});
});
Se você ler o livro da MooTools ou o MooTools tutorial
(ambos de minha autoria) eles começam de um lugar completamente
diferente. Enquanto você pode avançar e rapidamente aprender sobre
efeitos e DOM, se você quer aprender MooTools, você terá que começar
com coisas como Class, e vou admitir: se você
for novo em programação, ou simplesmente quer fazer alguma coisa
funcionar no seu site sem ter que aprender tudo sobre JavaScript, as
chances são de que jQuery pareça muito mais agradável para você.
Por outro lado, se você quer aprender JavaScript, MooTools é uma
grande forma de fazer isso. Ela implementa um monte de coisas que
JavaScript irá ter (muitos dos métodos em Natives são apenas
especificações do JavaScript 1.8
e além). Se você for familiarizado com programação, especialmente ambas
orientadas a objetos e programação funcional, MooTools possui um monte
de padrões de desenvolvimento que são muito excitantes e expressivos.
Qualquer coisa que você fizer, posso fazer melhor
Se você olhar as coisas que jQuery é capaz de fazer,
geralmente existe uma contrapartida equivalente na MooTools. Se você
olhar as coisas que a MooTools consegue fazer, geralmente não existe
forma de realizar usando jQuery porque seu código é voltado apenas ao
DOM. MooTools oferece uma gigantesca funcionalidade a mais que a
jQuery, mas não há nada na jQuery que o impeça de fazer essas coisas.
Por exemplo, jQuery não vem com nenhum tipo de sistema de herança, mas
tudo bem. Você pode, se quiser, utilizar a MooTools Class conjuntamente com jQuery (ou escrever a sua própria). Existe até um plug-in de herança para jQuery
(eu não utilizei, mas assumo que ele ofereça o mesmo tipo de tipo funcionalidade).
Se nós olharmos para o exemplo da jQuery acima:
$(document).ready(function() {
$("a").click(function(event) {
alert("Thanks for visiting!");
});
});
e quiséssemos traduzir para MooTools, teríamos:
window.addEvent('domready', function() {
$('a').addEvent('click', function(event) {
alert('Thanks for visiting!');
});
});
Existem muitas semelhanças, não?
Aqui um exemplo mais complexo da jQuery:
$(document).ready(function() {
$("#orderedlist li:last").hover(function() {
$(this).addClass("green");
},
function() {
$(this).removeClass("green");
});
});
e na MooTools:
window.addEvent('domready',function() {
$('#orderedlist li:last').addEvents({
mouseenter: function() {
this.addClass('green');
},
mouseleave: function() {
this.removeClass('green');
}
});
});
Novamente, muito semelhantes. Posso argumentar que a versão da
MooTools é mais explícita, mas também, devido a este motivo, é mais
longa. Está claro lendo o código da MooTools que estamos adicionando
dois eventos – um para onmouseenter e outro para onmouseleave; enquanto a versão jQuery é mais concisa, seu método hover
recebe duas funções – a primeira para onmouseenter e a segunda para onmouseleave. Pessoalmente, gosto do fato de a MooTools ser mais legível, mas essa é uma observação
muito subjetiva.
Vou dizer que às vezes o código da jQuery se torna muito exotérico
para o meu gosto. Para mim, os métodos nem sempre fazem sentido apenas
com uma breve olhada e acho difícil de entendê-los. Entretanto, isto é
de alguma forma injusto, uma vez que sou intimamente familiar com
códigos MooTools, então acho fácil de ler MooTools. Mas uma das coisas
que admiro na MooTools é como praticamente todos os nomes de métodos e
classes realmente informam sobre a sua função. Métodos são quase sempre
verbos e deixam poucas dúvidas sobre o que eles fazem. Toda linguagem
de programação requer que você vá à documentação para procurar sobre a
sintaxe quando você codifica – entretanto só estou dizendo que acho a
API da MooTools mais coerente e consistente.
MooTools permite você fazer da sua própria maneira
Mas e se você gosta da sintaxe da jQuery? Uma
forma de ilustrar o poder da MooTools é mostrar como é fácil alterá-la
ao seu gosto. Se nós quisermos implementar o método hover da jQuery na MooTools, poderíamos facilmente fazer:
Element.implement{{
hover : function(enter,leave){
this.addEvents({ mouseenter : enter, mouseleave : leave });
}
});
//and then you could use it exactly like the jQuery version:
$('#orderlist li:last').hover(function(){
this.addClass('green');
},
function(){
this.removeClass('green');
});
De fato, existem plug-ins da MooTools que fazem exatamente isso; oferecem a você a sintaxe da jQuery na MooTools.
O foco da MooTools em extensibilidade significa que você pode
implementar qualquer coisa que você queira. Isto é uma das coisas que a
jQuery não consegue fazer. A MooTools pode imitar a jQuery se quiser,
mas a jQuery não pode imitar a MooTools. Se você quiser escrever
classes, ou extender protótipos nativos, ou fazer alguma outra coisa
que a MooTools consegue fazer, você terá que fazer isso sozinho.
Encadeamento como um Design pattern
Vamos fazer outra dessa. Aqui um pouco de jQuery (do tutorial da jQuery):
$(document).ready(function() {
$('#faq').find('dd').hide().end().find('dt').click(function() {
$(this).next().slideToggle();
});
});
Este é um exemplo de sintaxe que eu não prefiro.
Olhando para o código acima, sou fortemente pressionado para ter
certeza do que ele está fazendo. Mais notavelmente, estou curioso sobre
o que end faz e como find, que o segue, está relacionado com o que end faz. Agora, olhando para a documentação da jQuery, fica claro o que
.end faz (ele reseta para o valor original do seletor,
neste caso #faq). Mas isso me parece muito estranho. Quando trabalho
com jQuery, geralmente me encontro incerto sobre o que um método irá me
retornar. Claramente isso não incomoda mais ninguém, pois a jQuery
possui um monte de pessoas a utilizando felizes da vida, então vou
novamente assumir isto como uma preferência pessoal.
Vamos ver a lógica acima com a MooTools:
window.addEvent('domready', function() {
var faq = $('faq');
faq.getElements('dd').hide();
faq.getElements('dt').addEvent('click', function() {
this.getNext().slide('toggle');
});
});
Novamente, o código da MooTools é um pouco mais
longo, mas é também mais explícito. Note também que o padrão de
desenvolvimento aqui é guardar a referência de #faq em uma variável,
onde jQuery usa seu método end para retorná-la. Vou mostrar que é possível escrever códigos altamente encadeados em MooTools. Por exemplo:
item.getElements('input[type=checkbox]')
.filter(function(box) {
return box.checked != checked;
})
.set('checked', checked)
.getParent()[(checked) ? 'addClass' : 'removeClass']('checked')
.fireEvent((checked) ? 'check' : 'uncheck');
Mas, realmente, escrever códigos como este em qualquer uma das frameworks, tenho que argumentar, é uma má pratica. É muito melhor encapsular sua lógica em trechos re-utilizáveis.
Reutilizando código com a jQuery
É muito tentador quando você está trabalhando em
um projeto codificar desta maneira. Simplesmente adicione na página um
pouco de lógica que seleciona os elementos do DOM e “configura-os”:
sumindo com alguns, alterando outros, adicionando eventos onclick e onmouseover em outros. Desenvolver códigos dessa forma é muito
eficiente, muito rápido. O problema de escrever toda sua lógica na declaração ondomready
é que você termina com um monte de código que faz a mesma coisa em
lugares diferentes. Se olharmos o padrão FAQ acima, poderíamos
facilmente aplicar a mesma lógica em qualquer lugar de uma outra página
com qualquer lista de termos e definições. Mas, afinal de contas, vamos
repetir a mesma lógica cada vez que precisarmos desse padrão?
Uma forma simples de criar códigos reutilizáveis é
agrupar a lógica em uma função e passar os argumentos. Aqui está como
isso pode ser feito na jQuery:
function faq(container, terms, definitions) {
$(container).find(terms).hide().end().find(definitions).click(function() {
$(this).next().slideToggle();
});
};
$(document).ready(function() {
faq('#faq', 'dd', 'dt');
});
Esta é uma forma muito melhor por dois grandes e importantes motivos:
- Se amanhã tivéssemos que mudar como essas
listas trabalham (talvez quiséssemos adicionar uma lógica de
monitoramento de clicks para podermos medi-los nos nossos logs ou
talvez quiséssemos procurar as definições através de AJAX), nós
poderíamos simplesmente mudar nosso método faq
principal e todos os lugares em que o utilizamos são automaticamente
atualizados. Ou se existir uma nova versão da jQuery que muda a forma
como as coisas trabalham, nós podemos atualizar apenas nosso método ao
invés de dúzias de cópias por todos os lugares. Chamo isso de keeping a small footprint nos meus aplicativos. Mantendo em menor número possível os pontos em que o
aplicativo é influenciado pelo código mais genérico, consigo deixar mais fácil o conserto de bugs, atualização de frameworks, inclusão de novos recursos ou
alterar funcionalidades. - O segundo motivo é que isso gera menos código.
Utilizando o mesmo método várias vezes, não repito o trabalho e isso é
importante em qualquer ambiente de trabalho. Isso também faz com que o
código que meus visitantes precisam pegar seja menor.
Na verdade, jQuery possui um sistema um pouco mais refinado de escrever widgets reutilizáveis como esse. Ao invés de encorajá-lo a colocá-lo em funções como no
exemplo acima (o que é realmente bastante primitivo), ela encoraja a escrita de jQuery plug-ins. Aqui está como poderia ficar:
jQuery.fn.faq = function(options) {
var settings = jQuery.extend({
terms: 'dt',
definitions: 'dd'
}, options);
//"this" is the current context; in this case, the elements we want to turn into faq layouts
$(this).find(settings.terms).hide().end().find(settings.definitions).click(function() {
$(this).next().slideToggle();
});
return this;
};
que você utilizaria através de:
$('#faq').faq();
Mas, olhando no exemplo acima, não há muita diferença entre definir nossa função faq dessa maneira ou declará-la como uma função stand alone. Bom, ela não está no
global namespace, mas poderíamos tão fácil e simplesmente
colocá-la em um namespace nosso próprio. Anexando-o à jQuery nós
podemos encadeá-lo com qualquer outro método jQuery. Outro benefício é
que o this nessa função é o mesmo do
contexto da jQuery, seja lá qual for ele no momento do encadeamento.
Utilizando este padrão para plug-ins, somos capazes de contruir nosso
plug-in de forma que ele se pareceça como parte da jQuery, mas na
verdade nosso plug-in é basicamente um simples função que pega algum
contexto da jQuery, faz algo com ele e então retorna o contexto para o
próximo item na cadeia. Não existe nenhuma grande complexibilidade
aqui, o que faz com que seja fácil para qualquer pessoa escrever um
plug-in para a jQuery – eles são apenas funções simples.
Note que é possível escrever plug-ins mais complexos na
jQuery como métodos e estados. Esse tipo de padrão é suportado com o
sistema de plug-in jQuery UI e que não utiliza o mesmo mecanismo do
plug-in básico (como o do nosso exemplo). Ao invés disso, você anexa um
objeto com métodos e propriedades no objeto jQuery (por exemplo $.ui.tabs).
Existe um atalho para chamar este objeto ($(selector).tabs()) de forma que você pode continuar encadeando da mesma forma que o plug-in faq.
Mas devido ao motivo de ele não retornar a referência para o objeto tabs
criado para o item no seu seletor, você é forçado a chamar este seletor
novamente para invocar seus métodos. Ao invés de chamar myTabInstance.add(url, label, index) você precisa executar o seletor novamente e chamar sua função pelo nome (como uma “string”): $(selector).tabs('add', url, label, index);.
Isso significa que você está executando o seletor duas vezes (a não ser
que você guarde-o em uma variável em algum lugar), e que você nunca tem
um pointeiro para o método add para que você possa fazer coisas como bind ou delay. Esse artigo é focado nos núcleos da MooTools e da jQuery e enquanto o sistema jQuery’s UI não
oferece essa funcionalidade, não é algo que venha com a jQuery por padrão.
Reutilizando código com a MooTools
Na MooTools, quando você quer definir um padrão, é mais provável você usar tanto a função Class ou implementar um método no objeto nativo (na String, por exemplo).
Ao invés de oferecer uma linguagem
completamente diferente do estilo nativo da JavaScript, a MooTools
tenta caminhar pela via do meio definindo sua própria sintaxe
personalizada e extendendo os padrões da JavaScript. Uma das formas que
ela faz isso é estendendo os protótipos dos objetos nativos na
linguagem e no DOM. Isso significa que se você precisar de um método trim para uma string, a MooTools encoraja você a adicionar esse método na String (note que String.trim já está na
MooTools, você não precisa incluí-la sozinho):
String.implement({
trim: function() {
return this.replace(/^\s+|\s+$/g, '');
}
});
Isso significa que você pode simplesmente executar " sem mais espaços no começo ou final! ".trim() e receber "sem mais espaços no começo ou final!". Alguém pode
dizer que implementar propriedades nos objetos nativos é inapropriado. Este é o motivo pelo qual a MooTools e a Prototype.js não se dão muito
bem entre elas – qualquer framework que manipular os protótipos nativos não irá se dar bem com outra que fizer o mesmo. Se eu definir String.prototype.foo()
e outra biblioteca fizer o mesmo na mesma execução, a que fizer isso
por último vencerá. De certa forma, isso é semelhante ao problema que
encontramos com o namespace global window.
Esta é a forma que a JavaScript funciona. Esta é a forma pela qual a JavaScript 1.8 adicionou tantos novos
recursos. Ela os adicionou nos protótipos.
Os desenvolvedores da MooTools criaram um robusto framework, o qual permite a você estendê-lo com suas próprias funcionalidades. Fizeram isso pensando que as pessoas
que o incluírem na sua página vão usá-lo, não outro framework. Na verdade, é um tanto rude exigir que um usuário faça o download de dois frameworks. A única
razão para incluir dois frameworks
é querer utilizar plug-ins de ambos, e na cabeça dos autores da
MooTools (inclusive na minha), se você quer um plug-in que não está
disponível na framework de sua escolha, é mais apropriado
você gastar um tempo codificando ele para o seu ambiente do que exigir
que os usuários peguem outro framework.
Uma vez que você aprenda como a JavaScript funciona e veja o poder
de estender objetos nativos, um novo nível de programação se abrirá
completamente. Você poderá escrever plug-ins que alteraram Elements,
Dates ou Functions. Enquanto alguns argumentam que incluir métodos aos
nativos dessa forma seja uma espécie de poluição, eu tenho que
argumentar que é dessa forma que a JavaScript foi criada para ser
utilizada. É um recurso padrão da linguagem. Anexar métodos para aos
nativos permite que o seu código seja conciso e compartimentado. A
jQuery faz isso também, mas limita suas melhorias ao protótipo do
objeto jQuery.
Enquanto você pode encadear múltiplas chamadas a métodos no objeto
jQuery, em qualquer outro tipo de objeto você tem que utilizar
genéricos. Por exemplo, na jQuery, se você quer remover os espaços do
começo e do final de uma string (trim) e iterar em linhas, você terá de escrever:
$.each( $.trim( $('span.something').html() ).split("\n"), function(i, line){alert(line);});
Mas como a MooTools modifica os protótipos, você pode:
$('span.something').get('html').trim().split("\n").each(function(line){alert(line);});
Apenas observando isso, vemos claramente o quão poderoso é alterar
os protótipos. Encadeamento nos elementos DOM não é o único lugar onde
o encadeamento é útil. A MooTools permite que você encadeie métodos em
qualquer objeto, inclusive rodar um método em vários elementos de uma
só vez.
A chave aqui é que no coração da MooTools framework mora a
noção que ela está lá para lhe ajudar a programar do jeito que você
quiser. Se existe alguma funcionalidade que não está no seu núcleo,
você pode estendê-lo da sua maneira. O trabalho do núcleo não é
oferecer cada bit de funcionalidade que vocês podem sequer querer um
dia, mas oferecer as ferramentas que permitem vocês escreverem as
coisas da maneira que vocês quiserem. Uma grande parte disso é deixar
fácil estender protótipos nativos e utilizar as vantagens da herança
prototipada. Você pode fazer essas coisas com JavaScript de baunilha,
mas a MooTools faz essa tarefa mais fácil e agradável.
MooTools e herança
Apesar do seu nome, a função Class da MooTools não é uma, nem cria classes. Ela possui padrões de desenvolvimento que podem lembrar as classes de linguagens de
programação mais tradicionais, mas, na verdade, Class é intimamente relacionada a objetos e herança prototipada.
Para criar um protótipo, você envia um objeto para a função Class dessa maneira:
var Human = new Class({
initialize: function(name, age) {
this.name = name;
this.age = age;
},
isAlive: true,
energy: 1,
eat: function() {
this.energy = this.energy + 1; //same as this.energy++
}
});
Você passa um objeto à Class (acima, nós passamos um objeto com membros como isAlive e eat) e esse objeto se torna o protótipo de todos os objetos do tipo Human. Para criar um Human, você faz assim:
var bob = new Human("bob", 20); //bob's name is "bob" and he's 20 years old.
Agora possuímos um descendente de Human, bob. bob possui as propriedades definidas no protótipo Human, mas é importante notar que, no início, essas propriedades existem
em bob apenas através da herança prototipada. Quando nós solicitamos bob.eat, bob não possui realmente um método eat. A linguagem JavaScript procura por um método
eat, não encontra e então ela sobe na cadeia de heranças até encontrá-lo no objeto Human, onde definimos Human.eat através da função Class. Isso ocorre também para
a propriedade energy. À primeira vista, isso parece potencialmente ruim; afinal de contas,
não queremos que todos os humanos que criemos ganhem o valor de bob.energy a cada vez que
bob come. O importante aqui é entender que na primeira vez que atribuirmos um valor para bob.energy, estaremos atribuindo o valor para uma propriedade que é realmente de bob e
assim a linguagem não irá mais procurar a propriedade nos objetos acima na cadeia de herança. Portanto, na primeira vez que bob come, ele recebe sua própria definição de energy (que é 2).
bob.eat(); //bob.energy == 2
Note que as propriedades name e age são próprias de bob; elas foram atribuídas a ele no momento que o criamos através da função initialize que passamos para Class.
Esse padrão pode parecer estranho para você, mas
o importante aqui é que podemos definir funcionalidades para um padrão
e criar instâncias desse padrão toda vez que precisarmos dele. Cada
instância mantém seu próprio estado. Então se criamos outras
instâncias, elas serão todas umas independentes das outras, mas
herdarão do mesmo protótipo:
var Alice = new Human();
//alice.energy == 1
//bob.energy == 2
As coisas começam a ficar realmente interessantes quando aumentamos esse comportamento.
Estendendo e implementando classes
Vamos rever nosso plug-in faq da
jQuery. O que aconteceria se quiséssemos incluir mais funcionalidades a
esse plug-in? E se quiséssemos fazer uma versão AJAX que procura as
respostas para as questões no servidor? Vamos imaginar que esse plug-in
faq foi escrito por outra pessoa e que gostaríamos de incluir mais coisas nele
sem alterá-lo de forma alguma (não queremos fazer um fork dele).
Nossas únicas chances reais são: ou duplicar a lógica do plug-in faq completamente (lembre-se, é apenas uma função), essencialmente criando um fork,
ou podemos chamá-la e depois incluir um pouco mais de funcionalidades
nela. Dadas as escolhas, a segunda parece nos salvar da maioria dos
problemas. O código ficaria mais ou menos assim:
jQuery.fn.ajaxFaq = function(options) {
var settings = jQuery.extend({
//some ajax specific options like the url to request terms from
url: ...definitions: 'dd'
},
options);
//"this" is the current context; in this case, the elements we want to turn into faq layouts
$(this).find(settings.definitions).click(function() {
$(this).load(.....); //the logic to load the content from the term
});
this.faq(); //call our original faq plug-in
});
Isso tem alguns contras. O primeiro de todos, nossa faq
irá repetir os seletores para as respostas, o que pode ser custoso; não
há nenhuma forma de guardar a resposta retornada e passá-la adiante
para uma segunda vez que precisemos dela. Segundo, nós não podemos
incluir nossa lógica AJAX no meio da lógica do plug-in faq a fim de
mostrar a resposta. O plug-in original chamou, slideToggle que expande a resposta usando um efeito. Isso é problemático porque esse efeito irá ocorrer antes de o
nosso AJAX terminar de carregar. Não há nenhuma solução aqui a não ser duplicar o plug-in faq por inteiro.
Agora vamos considerar nosso protótipo Human. Ele tem propriedades como isAlive e energy e um método chamado eat. O que acontece se quiséssemos fazer uma nova
versão de Human que tivesse mais propriedades? Com a MooTools, nós estendemos:
var Ninja = new Class({
Extends: Human,
initialize: function(name, age, side) {
this.side = side;
this.parent(name, age);
},
energy: 100,
attack: function(target) {
this.energy = this.energy - 5;
target.isAlive = false;
}
});
Você pode ver que incluímos um bocado de
funcionalidades aqui em um sub-protótipo. Esse sub-protótipo possui
todas as propriedades que são únicas para os ninjas. Ninjas
começam com um valor inicial energy de 100. Eles escolhem um side. Eles também possuem um método attack que os permitem matar outros Human, mas isso custa
energy.
var bob = new Human('Bob', 25);
var blackNinja = new Ninja('Nin Tendo', 'unknown', 'evil');
//blackNinja.isAlive = true
//blackNinja.name = 'Nin Tendo'
blackNinja.attack(bob);
//bob never had a chance
Deixando isso um pouco de lado, existem algumas coisas interessantes a se considerar. Note que ao definirmos Ninja através de Class, enviamos novamente uma função
initialize. Isso sobrescreve o método initialize de Human, mas ainda podemos acessar o método initialize de Human através da chamada this.parent com os
argumentos que o método initialize de Human espera. Além do mais, podemos controlar quando nossa lógica acontece; antes ou depois da chamada ao seu pai. Podemos
atribuir novos valores para as propriedades (como energy. por exemplo) e podemos definir nova funcionalidade. Imagine se pudéssemos fazer isso com nosso plug-in faq
da jQuery. Poderíamos carregar nosso AJAX e então disparar a animação (slideToggle).
A MooTools possui outro padrão chamado Mixin. Ao
contrário do relacionamento pai para filho que é definido estendendo um
protótipo em um sub-protótipo, você pode definir protótipos que são
misturados em outras classes para alterá-las com suas propriedades.
Veja um exemplo:
var Warrior = new Class({
energy: 100,
kills: 0,
attack: function(target) {
target.isAlive = false;
this.energy = this.energy - 5;
this.kills++;
}
});
Neste exemplo, nós separamos as características que fazem um Ninja diferente de um Human e as colocamos em um protótipo próprio. Isso nos permite reutilizar este
código independentemente de Ninja. Poderíamos então embutir nosso Ninja com as características de um Warrior:
var Ninja = new Class({
Extends: Human,
Implements: Warrior, //can be an array if you want to implement more than one
initialize: function(name, age, side) {
this.side = side;
this.parent(name, age);
}
});
Ninja ainda funcionará como anteriormente, mas o protótipo Warrior continua a nossa disposição para ser reutilizado:
var Samurai = new Class({
Extends: Human,
Implements: Warrior,
side: 'good'
});
Agora temos um protótipo Samurai e um Ninja. Mas olhe como foi necessário escrever pouco para definir tanto Ninja como Samurai. Ambos são semelhantes sob o
ponto de vista que são Human com qualidades de Warrior, mas são diferentes sob os aspecto do Samurai ser sempre bom, enquanto um Ninja pode pertencer a
ambos os lados. Gastando o tempo escrevendo Human e Warrior,
somos capazes de escrever três diferentes protótipos sem nenhuma
repetição de código e ainda mantemos um nível de controle cirúrgico
sobre quando os métodos são chamados e como estão relacionados entre
si. Cada instância que criamos possui seu próprio estado e seu código é
bem legível.
Agora que você teve uma visão geral de como os protótipos funcionam na MooTools, vamos olhar para nosso faq que escrevemos com a jQuery, escrevê-lo como
deveríamos na MooTools e estendê-lo para adicionar AJAX, da mesma forma que fizemos com a jQuery.
var FAQ = new Class({
//Options is another class provided by MooTools
Implements: Options,
//these are the default options
options: {
terms: 'dt',
definitions: 'dd'
},
initialize: function(container, options) {
//we store a reference to our container
this.container = $(container);
//setOptions is a method provided by the Options mixin
//it merges the options passed in with the defaults
this.setOptions(options);
//we store the terms and definitions
this.terms = this.container.getElements(this.options.terms);
this.definitions = this.container.getElements(this.options.definitions);
//we call our attach method
//by breaking this into its own method
//it makes our class easier to extend
this.attach();
},
attach: function(){
//loop through the terms
this.terms.each(function(term, index) {
//add a click event to each one
term.addEvent('click', function(){
//that calls our toggle method for
//the current index
this.toggle(index);
}, this);
}, this);
},
toggle: function(index){
//toggle open the definition for the given index
this.definitions[index].slide('toggle');
}
});
Uau! É um bocado de código. Mesmo se removermos todos os comentários, ainda teremos duas dúzias de linhas de código. Já mostrei anteriormente que podemos construir esse plug-in com aproximadamente a mesma quantidade de código que na versão da jQuery. Então porquê essa é tão mais comprida? Bem, fizemos dela muito mais flexível. Para usar este protótipo, podemos apenas chamar o construtor, dessa maneira:
var myFAQ = new FAQ(myContainer);
//and now we can call methods on it if we want:
myFAQ.toggle(2); //toggle the 3rd element
Podemos acessar métodos e propriedades dessa instância. Mas e sobre nossa funcionalidade AJAX? O problema com nossa extensão AJAX na versão da jQuery é que não podíamos atrasar a animação até o término do carregamento do conteúdo. Não temos este problema com nossa versão na MooTools.
var FAQ.Ajax({
//this class inherits the properties of FAQ
Extends: FAQ,
//it also gets a new option in addition to the other defaults
//this one for url, that we're going to append the index of the
//term to; in reality we might make this more robust, but for
//this example it serves the purpose
options: {
url: null;
},
//we're going to cache the results, so if a section is opened
//twice, we won't hit the server for the data
indexesLoaded: [],
toggle: function(index){
//if we've already loaded the definition
if (this.indexesLoaded[index]) {
//just call the previous version of toggle
this.parent(index);
} else {
//otherwise, request the data from the server
new Request.HTML({
update: this.definitions[index],
url: this.options.url + index,
//and when the data is loaded, expand the definition
onComplete: function(){
this.indexesLoaded[index] = true;
this.definitions[index].slide('toggle');
}
}).send();
}
}
});
Agora temos uma versão da nosso protótipo FAQ que permite procurar respostas no servidor. Note que somos capazes de integrar a nova lógica de uma forma que não anima a resposta até depois do conteúdo voltar do servidor (o que não conseguíamos fazer na versão com a jQuery). Note também que nós apenas tivemos descrever a nova funcionalidade (AJAX) e escrever um pouco mais. Essa extensibilidade permite a criação de famílias de plug-ins que ofereçam diferentes formas de funcionalidade. Ela permite também que você utilize o plug-in de outra pessoa e altere, se precisar, apenas alguns bits que você gostaria que fossem diferentes (sem precisar fazer um fork). Isso ajuda a explicar o porquê, para cada funcionalidade (seletores de data, interface de abas, etc), você encontra apenas alguns plug-ins para a MooTools. A maioria dos plug-ins que você pega, ou resolve seu problema, ou você simplesmente pode melhorá-los com as coisas que você precisa.
Como mostrei anteriormente, é possível escrever widgets complexos na jQuery também. Entretanto, ao fazer isso longe dos assuntos relacionados ao DOM, a maioria do código que você cria é JavaScript de baunilha. A modelagem da jQuery não oferece nenhum sistema para extensão de protótipos, nem ajuda a fazer Mixins que podem ser facilmente re-utilizados. Finalmente, os plug-ins da jQuery são sempre atribuidos à elementos do DOM. Se você quiser escrever uma classe que, por exemplo guarda as URLs processadas, não há nenhum sistema que lhe ajude nessa tarefa a não ser que você mesmo o faça.
A hora da decisão
A jQuery se foca em expressividade, codificação “rápida e fácil” e DOM. MooTools se foca em extensibilidade, herança, legibilidade, reuso e sustentabilidade. Se você analisar essas coisas na forma de vias opostas, o lado da jQuery leva para algo que é fácil de utilizar e traz resultados rápidos, mas (na minha experiência) pode se transformar em códigos difíceis de se sustentar e reutilizar (mas isso depende apenas de você, lembro que não é um problema que a jQuery se propõe a resolver), enquanto o lado da MooTools leva a um processo de aprendizado mais demorado e exige que você escreva mais antes de obter os resultados, mas no final das contas é mais reutilizável e sustentável.
Além do mais, nem o núcleo da MooTools, nem o núcleo da jQuery possuem todos os recursos que você possa imaginar. Ambas as frameworks mantêm seus núcleos bem enxutos deixando como tarefa para seus usuários escrever plug-ins e extensões. O trabalho deles não é oferecer todos os recursos que você pode querer, mas oferecer as ferramentas para que você possa criar qualquer coisa que você imagine. Este é o poder da JavaScript, e das frameworks JavaScript em geral, e nesta tarefa, ambas as frameworks se excedem. A MooTools alça um vôo mais alto e oferece ferramentas para você escrever qualquer coisa que imaginar dentro e além dos domínios do DOM, mas paga como preço possuir uma curva de aprendizado mais lenta. Essas ferramentas são um conjunto maior do que as oferecidas pela jQuery, mas o foco da jQuery em criar uma API rápida e fácil, não impede que você utilize os métodos de herança nativos da JavaScript ou utilize o “sistema de classes” da MooTools se você quiser.
Este é o motivo pela qual digo que ambas as frameworks são escolhas excelentes. Meu esforço era destacar as diferenças de filosofia entre essas duas base de código, suas vantagens e desvantagens. Duvido que eu tenha conseguido manter minha imparcialidade com relação à MooTools, mas espero que este artigo tenha sido útil. Independentemente da framework que você escolher agora, você já sabe bastante sobre as duas. Se você tiver o luxo do tempo, recomendo que você crie um site com cada uma delas. Então escreva sua própria análise delas e talvez suas conclusões destaquem alguma coisa que esqueci.