JavaScript com café, parte 2 - Tableless - Website com artigos e textos sobre Padrões Web, Design, Back-end e Front-end tudo em um só lugar.
por Davi Ferreira 29/05/2012 ~ 5 min. / 1027 palavras
No artigo anterior vimos as funcionalidades básicas da linguagem CoffeeScript. Falamos sobre variáveis, funções, objetos, condicionais, loops e integração com jQuery. Neste artigo, veremos algumas implementações mais avançadas, com destaque para o conceito de classes.
Uma função em CoffeeScript pode receber um ou mais parâmetros definidos em sua especificação. Através de splats (…) podemos definir também parâmetros variáveis, ou seja, você pode passar quantos parâmetros quiser. Estes parâmetros serão convertidos pelo compilador CoffeeScript em um array:
[cce lang="coffeescript"]lista_autores = (site, autores…) ->
$("#nome-site").text site
$("#autores").text autores.join("," )[/cce]
O código acima resulta no seguinte JavaScript:
[cce lang="javascript"]var lista_autores,
lista_autores = function() {
var autores, site;
site = arguments[0], autores = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
$("#nome-site").text(site);
return $("#autores").text(autores.join(","));
};[/cce]
Poderíamos, então, chamar a função lista_autores passando n autores nos argumentos:
[cce lang="coffeescript"]lista_autores "Tableless", "Davi Ferreira", "Diego Eis", "Zeno Rocha"
lista_autores "DaviFerreira.com", "DaviFerreira", "Chico Stallone"[/cce]
Em JavaScript, classes e objetos são uma coisa só. Um objeto não é uma instância de uma classe, como em outras linguagens. Podemos dizer que tudo é um objeto e existem prototypes. Um prototype é um objeto que possui suas propriedades compartilhadas por outros objetos. CoffeeScript faz uso deste recurso para criar Classes e Objetos.
Vejamos a declaração de uma classe:
[cce lang="coffeescript"]class Jogador
constructor: (nome, time) ->
@jogos = []
Jogador.total_jogadores++
total_jogos: ->
console.log @jogos.length
adiciona_jogo: (jogo) ->
@jogos.push(jogo)
@total_jogadores = 0[/cce]
Desconstruindo o código, o constructor da classe, compilado em JavaScript, vira uma função com o mesmo nome do objeto. Os métodos total_jogos e adiciona_jogo são prototypes do objeto Jogador. E, por fim, a variável total_jogadores faz referência à classe Jogador, ou seja, ao objeto original que dá origem às instâncias de jogadores. Por exemplo:
[cce lang="coffeescript"]j1 = new Jogador "Zico", "Flamengo"
j1.nome # "Zico"
j1.time # "Flamengo"
Jogador.total_jogadores # 1
j2 = new Jogador "Roberto Dinamite", "Vasco"
j2.nome # "Roberto Dinamite"
j2.time # "Vasco"
Jogador.total_jogadores # 2[/cce]
Portanto, existem três diferentes tipos de propriedades em uma classe CoffeeScript: 1) o constructor da classe, 2) métodos (prototypes) e propriedades referentes à uma instância da classe e 3) métodos e propriedades referentes ao objeto "pai" da classe.
Em JavaScript, a herança entre classes é feita através do encadeamento de prototypes. Esse encadeamento pode ser bem confuso quando falamos de muitos objetos. A linguagem CoffeeScript resolve esse problema implementando o recurso extends.
[cce lang="coffeescript"]class Goleiro extends Jogador
super nome, time
@gols_sofridos = []
Goleiro.total_goleiros++
adiciona_gol_sofrido: (gol) ->
@gols_sofridos.push(gol)
total_gols_sofridos: ->
console.log @gols_sofridos.length
@total_goleiros = 0[/cce]
Notem a chamada especial super, que invoca o método constructor da classe Jogador. Um objeto Goleiro herda todos os métodos da classe pai (Jogador) e pode ainda sobrescrever e/ou ter seus próprios métodos e propriedades.
É comum uma aplicação CoffeeScript vir separada em módulos. Cada módulo, como vimos no artigo anterior, está encapsulado em uma função anônima autoexecutável. E se quisermos compartilhar variáveis e funções entre os módulos?
Para isso, precisamos definir um escopo global, criando um elemento root, por exemplo.
[cce lang="coffeescript"]root = global ? window
root.site = "Tableless"
root.lista_autores = (autores) ->
console.log autores.join(", ")[/cce]
Dessa forma, qualquer módulo tem acesso à variável site e à função lista_autores.
Assim como outros exemplos, um switch em CoffeeScript é infinitamente mais bonito e legível do que sua parte original em JavaScript. Como em Ruby, um switch CoffeeScript aceita múltiplos argumentos em um caso:
[cce lang="coffeescript"]switch time
when "Flamengo" then console.log "Primeira Divisão"
when "Vasco", "Botafogo" then console.log "Segunda Divisão"
when "Fluminense" then console.log "Terceira Divisão"[/cce]
O resultado em JavaScript:
[cce lang="javascript"]switch (time) {
case "Flamengo":
console.log("Primeira Divisão");
case "Vasco":
case "Botafogo":
console.log("Segunda Divisão");
case "Fluminense":
console.log("Terceira Divisão");
Operador existencial
Mais uma vez inspirada em Ruby, CoffeeScript implementa o operador existencial "?". Este operador retorna true exceto se a variável testada for null ou undefined.
[cce lang="coffeescript"]time = "Flamengo" if primeira_divisao? and campeao_mundial?[/cce]
[cce lang="javascript"]var time;
if ((typeof primeira_divisao !== "undefined" && primeira_divisao !== null) && (typeof campeao_mundial !== "undefined" && campeao_mundial !== null)) {
time = "Flamengo";
Como vimos acima ela também pode ser utilizada para definir variáveis. O exemplo abaixo define a variável site com root.site se essa última existir, caso contrário utiliza o valor "Tableless".
[cce lang="coffeescript"]site = root.site ? "Tableless"[/cce]
O operador condicional pode também ser utilizado para evitar uma quebra em encadeamento de métodos. Por exemplo:
[cce lang="coffeescript"]autor = artigo.retorna_autor?().total_artigos?.nome [/cce]
[cce lang="javascript"]var autor, _ref;
autor = typeof artigo.retorna_autor === "function" ? (_ref = artigo.retorna_autor().total_artigos) != null ? _ref.nome : void 0 : void 0;[/cce]
Desse modo, ao invés de TypeError, o código acima retornaria undefined quando qualquer um dos métodos da cadeia for null ou undefined.
Através do console podemos executar todas as operações do compilador e gerar nosso código JavaScript. No entanto, algumas ferramentas facilitam esse trabalho através de interfaces gráficas:
Meu favorito é o Live Reload, disponível como uma app para Mac e Windows e um comando para Linux. Além de compilar CoffeeScript, o LiveReload utiliza extensões para os navegadores e recarrega a página quando qualquer alteração é realizada no código (não apenas CoffeeScript).
Lançado recentemente, o aplicativo CodeKit, disponível apenas para Mac, é um concorrente do LiveReload e, além de monitar alterações e recarregar páginas no navegador, conta também com integrações com frameworks, otimização de imagens e validação (lint) de código.
Outra ferramenta interessante é a extensão CoffeeConsole, para Chrome. Esta extensão adiciona um novo painel ao Chrome Developer Tools, possibilitando a execução de códigos CoffeeScript, similar ao console JavaScript já integrado na ferramenta.
Mais CoffeeScript
Essa foi uma visão geral da linguagem CoffeeScript que, recentemente, entrou para o ranking das dez linguagens mais populares no GitHub, desbancando ObjectC.
Finalizando, para aprender mais sobre CoffeeScript, indico os seguintes recursos:
The Little Book on CoffeeScript – Livro disponível na íntegra online e em versões impressa, PDF e para Kindle via O'Reilly.
A sip of CoffeeScript – Curso interativo do pessoal da CodeSchool com vídeos e exercícios online. Bem legal!
Smooth CoffeeScript – Outro livro disponível de graça online e também em PDF. Possui ainda uma versão interativa em HTML5.