Você está na página 1de 80

i

Javascript Avanado
Elcio Ferreira
elcio@visie.com.br
Introduo

Este livro foi escrito para um evento. O primeiro Workshop de


Javascript Avanado da Visie, que aconteceu em 22 de maro de
2012 em So Paulo. A princpio, ele deveria ser uma simples
apostila, mas conforme ele foi sendo escrito, ficou claro para mim
que poderia ajudar muita gente, muito alm do prprio evento.

Por isso resolvi transform-lo num e-book. Estamos vivendo tempos


interessantes para o programador Javascript. At pouco tempo,
Javascript era uma linguagem de programao de "segunda linha",
cujo uso era suportado pelos programadores porque era a nica
maneira de se programar no navegador. Isso mudou radicalmente.

No mundo inteiro, mais e mais programadores se dedicam a


Javascript hoje, aumentando o conhecimento disponvel sobre a
linguagem e a maturidade das ferramentas que temos para
desenvolver. Ao mesmo tempo, temos a vinda do HTML5, a
popularizao das plataformas mveis capazes de executar
Javascript de primeira classe, como o iPhone OS e o Android, e a
adoo do Javascript nos mais diversos ambientes, por exemplo na
construo de interfaces para o novo Windows 8 ou no servidor com
Node.js.

Se voc j estudou o bsico de Javascript e est procurando para


onde ir agora, este o livro para voc.
Estruturas de Dados em Javascript

Uma das primeiras coisas que todo programador deve aprender ao


lidar com uma linguagem como funcionam suas estruturas de
dados. Na maioria das linguagens modernas, h duas estruturas
bsicas de dados, que se repetem em Javascript: o array (ou matriz,
ou lista) e o hash (ou dicionrio).

Arrays
Em Javascript podemos criar arrays com a seguinte sintaxe:
meuArray=["Spock", 42, false, ""]

A sintaxe a seguir faz a mesma coisa:


meuArray=Array("Spock", 42, false, "")

E tambm essa:
meuArray=new Array("Spock", 42, false, "")

Arrays em javascript implementam os padres de fila e pilha, com os


mtodos pop, push, shift e unshift. Alm disso, em navegadores
modernos, arrays tambm implementam o padro Map/Reduce. Em
navegadores "no modernos" (leia-se Internet Explorer) o padro
Map/Reduce pode ser implementado usando-se a biblioteca js-
methods. No captulo 15 vamos olhar de perto como criar nossa
prpria biblioteca para extender objetos nativos, e voc vai entender
melhor como a js-methods funciona. Por hora, basta saber que ao
inclu-la na pgina os arrays no Internet Explorer vo ganhar os
mtodos map, reduce e uma srie de outros.

Vale a pena dar uma olhada no padro map / reduce. Para


implement-lo, uma linguagem deve oferecer em seus arrays os
mtodos map, reduce e, opcionalmente, filter. O mtodo map recebe
uma funo como parmetro e executa essa funo tantas vezes
quantas forem os itens do array, passando cada item do array como
argumento para a funo. O retorno de map um array com os
retornos da funo. Veja funcionando:
function quadrado(n){
return n*n
}

valores=[2, 4, 6, 8, 10]

alert(valores.map(quadrado)) // Mostra: [4, 16, 36, 64, 100]

A funo filter tem sintaxe parecida, e tambm executa a funo


recebida como parmetro uma vez para cada um dos valores do
array. Ela cria um novo array com os elementos do array original
cuja execuo retornou true (ou qualquer valor que faz casting para
true.) Veja um exemplo:
function par(n){
return n%2==0
}

valores=[13, 16, 19, 22, 25, 28]

alert(valores.filter(par)) // Mostra: [16, 22, 28]

J a funo reduce tambm recebe uma funo como parmetro, mas


esta deve receber dois parmetros. A funo ser chamada
recebendo como parmetros os dois primeiros elementos do array e
seu valor de retorno ser armazenado. Em seguida ela ser chamada
recebendo como primeiro parmetro o retorno armazenado e como
segundo parmetro o prximo elemento do array. Ela ser assim
chamada at o fim do array. Veja um exemplo:
function multi(a,b){
return a*b
}

lista=[1,2,3,4,5]

alert(lista.reduce(multi)) // Mostra: 120

Hashs, ou objetos literais.


Alm dos arrays de ndice numrico, a maioria das linguagens de
programao implementa uma estrutura de dados conhecida como
hash, dicionrio ou array associativo. semelhante a um array, mas
seus ndices so strings. uma estrutura de dados muito comum, e
muito til. Em Javascript, porm, os hashs so desnecessrios. Isso
porque em Javascript, todos os objetos so completamente
dinmicos, podendo ganhar propriedades novas, com qualquer nome
e tipo, a qualquer momento. Assim, ao invs de ter um tipo diferente
de dados para guardar estruturas, voc simplesmente as armazena
em objetos.

Em PHP, por exemplo, voc poderia ter uma estrutura assim:


$pessoa=array(
'nome' => 'Joaquim Silva',
'idade' => 31,
'email' => 'jsilva@fbi.gov'
);

// Mostra o nome:
echo $pessoa['nome'];

Em Javascript isso no necessrio, pois voc pode criar um objeto


dinamicamente para armazenar esses dados, assim:
pessoa=new Object()
pessoa.nome='Joaquim Silva'
pessoa.idade=31
pessoa.email='jsilva@fbi.gov'

// Mostra o nome:
alert(pessoa.nome)

Claro, essa sintaxe no das mais elegantes e voc precisa escrever


um bocado. Por isso, o Javascript tem uma outra maneira de
declarar objetos:
pessoa={
nome:'Joaquim Silva',
idade:31,
email:'jsilva@fbi.gov'
}

// Mostra o nome:
alert(pessoa.nome)
Chamamos objetos criados com essa sintaxe de "objetos literais".
Por fim, ao invs de acessar as propriedades do objeto com a
sintaxe:
pessoa.nome

Voc pode usar:


pessoa['nome']

Isso faz com que os objetos em Javascript possam se comportar


exatamente como os hashs em outras linguagens, sem contudo
deixar de ser objetos. Voc vai ver como isso valioso nos captulos
6 e 7.

Inteligncia nos dados


Em seu clssico The Art of Unix Programming, Eric S. Raymond nos
aconselha a colocar conhecimento nos dados, de modo a tornar a
lgica de nossos programas estpida e robusta. difcil, numa
primeira leitura, entender o valor desse conselho. Por isso, gostaria
de comear com um exemplo bem simples. Imagine que voc precisa
desenvolver uma calculadora de impostos que aplique a seguinte
regra:

1. Para compras at R$ 1070,00 a alquota ser de 2,3%


2. Para compras acima de R$ 1070,00 at 2340,00 a alquota
ser de 2,1%
3. Para compras acima de R$ 2340,00 at R$ 5700,00, a alquota
ser de 1,9%
4. Para compras acima de R$ 5700,00 a alquota ser de 1,7%

Voc poderia implementar isso dessa forma:


function calculaImposto(valor){
if(valor<=1070)return valor*2.3/100
if(valor<=2340)return valor*2.1/100
if(valor<=5700)return valor*1.9/100
return valor*1.7/100
}

Parece bastante simples, no? O problema que escrever esse


cdigo envolve um bocado de trabalho braal. E se os valores das
alquotas mudam ou surge uma nova faixa de alquotas, vai dar
algum trabalho encontrar esses valores no cdigo e modific-los. Se
voc precisar publicar uma tabela com essas alquotas, vai ter que
repetir essa inteligncia numa funo s um pouquinho diferente.
Agora veja a mesma coisa com uma abordagem como a sugerida por
Raymond:
aliquotas=[
[ 0, 2.3],
[1070, 2.1],
[2340, 1.9],
[5700, 1.7],
]

function calculaImposto(valor){
for(var i=aliquotas.length-1;i>=0;i--)
if(valor>aliquotas[i][0])
return valor*aliquotas[i][1]/100
}

Agora digamos que voc quisesse mostrar, em outro lugar, uma


tabela com as alquotas de impostos. Com a primeira abordagem,
teria que escrever essa tabela, duplicando os dados de alquotas. E
quando alguma coisa mudasse, teria de se lembrar de alterar em
dois lugares. Com a segunda abordagem pode fazer algo como:
function tabela(){
var tabela="<table class='aliquotas'>\n"
tabela+="<tr><th>valor inicial</th><th>alquota</th></tr>"
tabela+=aliquotas.map(function(i){
return "<tr><th>R$ "+i[0]+"</th><td>"+i[1]+"%</td></tr>"
}).join("\n")
tabela+="\n</table>"
return tabela
}

Raymond cita Rob Pike:

Dados dominam. Se voc escolheu as estruturas de dados corretas e


organizou as coisas bem, os algoritmos sero quase sempre auto
evidentes. Estruturas de dados, no algoritmos, so centrais para a
programao.
JSON, Ajax e JSONp

Como vimos no captulo anterior, as duas estruturas bsicas de


dados em Javascript so o array e o objeto literal, que faz o papel de
dicionrio. Tambm vimos sua sintaxe, que bastante simples. Para
descrever uma lista de classes e alunos, poderamos ter algo como:
classes=[ {
'description': '1 A',
'year': 1,
'students': [ {
'name': 'Pedro Souza',
'email': 'psouza@cia.gov'
}, {
'name': 'Manoel Soares',
'email': 'msoares@kgb.gov.ru'
}, {
'name': 'Joaquim Silva',
'email': 'jsilva@fbi.gov'
}
]
}, {
'description' : '2 A',
'year': 2,
'students': [ {
'name': 'Maria Souza',
'email': 'msouza@cia.gov'
}, {
'name': 'Ftima Soares',
'email': 'fsoares@kgb.gov.ru'
}, {
'name': 'Tnia Silva',
'email': 'tsilva@fbi.gov'
}
]
}
]

O que um formato bastante simples de ler e escrever, e ao mesmo


rico em significado e muito flexvel. Alm disso, baseado em texto.
Logo, fcil de armazenar e transmitir pela rede. Por isso, algum
teve a feliz ideia de usar esta mesma notao de dadosfora do cdigo
Javascript. Deu-se a isso o nome de JSON, sigla para Javascript
Object Notation.

Uma string ou um arquivo JSON uma representao estruturada


de dados, assim como uma string ou arquivo XML. A estrutura
acima seria descrita como JSON assim:
[ {
'description': '1 A',
'year': 1,
'students': [ {
'name': 'Pedro Souza',
'email': 'psouza@cia.gov'
}, {
'name': 'Manoel Soares',
'email': 'msoares@kgb.gov.ru'
}, {
'name': 'Joaquim Silva',
'email': 'jsilva@fbi.gov'
}
]
}, {
'description' : '2 A',
'year': 2,
'students': [ {
'name': 'Maria Souza',
'email': 'msouza@cia.gov'
}, {
'name': 'Ftima Soares',
'email': 'fsoares@kgb.gov.ru'
}, {
'name': 'Tnia Silva',
'email': 'tsilva@fbi.gov'
}
]
}
]

Imagine um arquivo com o contedo acima, ele pode ser lido e


interpretado, em diversas linguagens de programao, gerando uma
estrutura de dados, exatamente como voc poderia, em qualquer
linguagem, ler um arquivo XML ou CSV. No site oficial do formato
JSON h bibliotecas para leitura e escrita de JSON em um grande
nmero de linguagens.

Em relao ao uso de XML, JSON mais leve, fcil de entender, e


para a maioria das aplicaes to flexvel quanto XML. Por isso,
muitos desenvolvedores tem preferido trabalhar com dados em
JSON. Outros, ao publicar webservices, disponibilizam os dados em
XML e em JSON, de modo que quem consome o webservice pode
escolher que formato usar.

Algumas palavras sobre Ajax


Na ltima dcada a web evoluiu de uma plataforma para a
publicao de hipertexto simples para uma verdadeira plataforma
global de aplicaes. Parte dessa evoluo se deve inveno do
Ajax. Num artigo de 2005, Jesse James Garret cunhou o termo,
dando nome a uma tcnica que j vinha sendo usada por muitos
desenvolvedores ao redor do mundo.

Ajax a sigla para "Asynchronous Javascript and XML". Uma tcnica


que permite a carga de contedo do servidor pelo javascript, em
segundo plano, sem recarregar a pgina. Garret colocou XML no
nome, porque era o formato de dados mais comum na poca,
embora no fosse o nico. Com Ajax, voc pode carregar qualquer
tipo de dado textual do servidor, incluindo CSV, texto puro, HTML
e, claro, JSON.

Vamos ver, rapidamente, como JSON pode tornar mais fcil nosso
trabalho com Ajax. Imagine que voc tem uma aplicao em que o
usurio preenche alguns campos de busca e precisa ver uma lista de
alunos. Vamos usar como exemplo PHP. Voc pode escrever um
arquivo PHP que recebe via GET os dados da busca e seleciona a
lista de alunos do banco de dados e obtm, como resultado, um
array. muito fcil fazer isso com PDO, por exemplo. Em seguida,
sua pgina PHP pode fazer:
echo json_encode($lista_de_alunos);

Claro, vale a mesma coisa se voc estiver trabalhando com qualquer


outra linguagem. A maioria das bibliotecas para linguagens
dinmicas permite a converso de dados nativos para JSON em uma
linha. No client, voc pode usar um mtodo jQuery para obter esses
dados:
$.getJSON(

'/consulta_lista_de_alunos.php?nome='+nome,

function(resultados){

// Como a jQuery faz a converso de JSON automaticamente


// a varivel resultados no contm uma string JSON ou
// coisa parecida. Ela contm um array comum, com objetos
// nativos do Javascript dentro:

for(var i=0;i<resultados.length;i++){
$("#lista").append("<li>"+resultados[i].nome+"</li>")
}

Trabalhando com Ajax e JSON, na prtica, voc consegue trazer seu


array do PHP para o Javascript, sem ter que se preocupar com a
converso dos dados.

JSONp, Ajax crossdomain simplificado


Acesse essa URL: http://search.twitter.com/search.json?
q=javascript

O que voc vai ver o resultado de uma busca no Twitter (buscando


por "javascript") em formato JSON. Assim, da mesma maneira que
voc transfere seus arrays do PHP para o Javascript de sua pgina, o
Twitter deixa voc obter arrays com os resultados de busca. O
problema que voc no pode fazer requisies Ajax para outro
domnio. Por uma questo de segurana, se voc tentar requisitar a
URL acima a partir de uma pgina qualquer, voc vai obter um erro.
O navegador s deixa voc fazer requisies Ajax para o mesmo
domnio em que suas pginas esto hospedadas.

Na verdade, existe uma maneira de, em navegadores mais novos,


publicar webservices que podem ser consumidos por Javascript em
qualquer domnio. Vamos falar mais disso no prximo captulo. Mas
ainda uma tecnologia nova, suportada por poucos navegadores.

A soluo hoje implementar um proxy. Trata-se de um script PHP


no servidor que vai requisitar (com fopen) a URL do twitter e
retornar os dados para o navegador. Assim, o navegador faz uma
requisio para uma pgina dentro do prprio domnio da aplicao,
e esta pgina quem fala com o Twitter. Claro, esse no o melhor
dos mundos. Alm de essa tcnica dar um pouquinho de trabalho, a
performance fica prejudicada, pois seu servidor precisa ficar
"repetindo" os dados, como um papagaio.

Felizmente nossos amigos no Twitter nos deram outra opo. Ela se


chama JSONp, sigla para JSON with padding. Implementando o
padro JSONp o twitter fez que as URLs que retornam JSON
possam receber um parmetro callback, assim:

http://search.twitter.com/search.json?
q=javascript&callback=showTwits

O resultado praticamente o mesmo. A nica diferena a chamada


funo showTwits::
showTwits({"completed_in":0.083,"max_id":175028144657539072...

Agora, lembre que JSON um formato de dados baseado na sintaxe


do Javascript. Em outras palavras, JSON Javascript. Ao colocar
essa chamada de funo antes dos dados JSON o Twitter est nos
dando a possibilidade de incluir este arquivo na pgina como um
script, ao invs de fazer uma requisio Ajax e tentar ler o valor
JSON.

Assim, para usar esses dados em uma pgina, basta incluir uma nova
tag script com src preenchido com a URL acima e a funo
showTwits ser executada tendo como argumento os dados em
JSON. Veja como poderamos exibir o contedo das mensagens:
<!DOCTYPE html5>
<html>
<head>

<meta charset="UTF-8">
<title>Exemplo de JSONp com Twitter</title>

<script>
// Esta a funo de mostrar os resultados. O nome dela
// deve ser passado na query string, no parmetro callback.
function showTwits(r){

// Monta um HTML com os resultados. Note que o valor de r


// um objeto do Javascript, no uma string JSON.
var rHTML="<ul>"
for(var i=0;i<r.results.length;i++){
var t=r.results[i]
rHTML+="<li><strong>"+t.from_user_name+"</strong>: "+
t.text+"</li>"
}
rHTML+="</ul>"

// Mostra o HTML gerado.


document.getElementById("twbusca").innerHTML=rHTML
}

function busca(q){
// Mensagem de carregando.
document.getElementById("twbusca").innerHTML='Carregando...'

// Monta a URL incluindo o que o usurio digitou na busca.


var url='http://search.twitter.com/search.json?q='+q+
'&callback=showTwits'
// Cria um novo elemento <script>.
s=document.createElement('script')
s.setAttribute('src',url)

// Insere esse script no documento.


document.getElementsByTagName('head')[0].appendChild(s)

// Cancela o submit
return false
}
</script>

</head>
<body>

<form onsubmit="return busca(document.getElementById('q').value)">


<input id="q" autofocus><input type="submit" value="Buscar">
</form>

<div id="twbusca"></div>

</body>
</html>

Listagem do arquivo exemplo 02.01

E veja como fica o resultado:

Claro, para usar essa tcnica, necessrio que quem publicou o


webservice suporte o formato JSONp. A boa notcia que o formato
est se tornando bastante popular. Enquanto estou escrevendo, o
site ProgrammableWeb lista 329 APIs que suportam JSONp,
incluindo empresas como AddThis, Facebook, Flickr, Foursquare,
GitHub, Google, Instagram, Instapaper, LinkedIn, Livestream,
Vimeo, Wikimapia e Yahoo. E, claro, se voc est publicando um
servio na web, se vai publicar URLs para consumo por Ajax,
recomendo que estude a possibilidade de oferecer suporte a JSONp.
O trabalho muito pequeno e abre muitas possibilidades.
CORS

Como voc deve ter percebido no captulo anterior, requisies Ajax


so uma tcnica muito til para od esenvolvimento de aplicaes
web, mas as limitaes de segurana que no permitem requisies
para outro domnio nos impedem de integrar com facilidade nossa
aplicao a servios de terceiros. Por outro lado, bastante
importante que hajam restries de requisio entre domnios. Se
no houvesse essa importante restrio, isso arruinaria a
privacidade da maior parte dos servios web.

Imagine, por exemplo, que eu pudesse incluir em meu site


requisies Ajax a outros domnios e voc o visitasse estando logado
no Facebook. Eu poderia fazer programar requisies ao Facebook
para capturar seu nome de usurio, seus dados de perfil, suas
mensagens, lista de amigos e o que mais eu quisesse.

Same Origin Policy


Por isso, a equipe de desenvolvimento do Netscape 2 criou, h mais
de quinze anos, uma poltica de acesso aos elementos de um site
usada at hoje, conhecida como "Same Origin Policy" (Poltica de
Mesma Origem.) A partir de ento, os navegadores s permitem o
compartilhamento de recursos entre pginas se elas tiverem
exatamente o mesmo protocolo, o mesmo domnio e a mesma porta.

Por exemplo, a pgina http://servidor.com.br/pagina/ poderia


acessar:

1. http://servidor.com.br/pagina_dois/
2. http://servidor.com.br/outra/pagina/acessivel.html

Mas no poderia acessar:

1. https://servidor.com.br/pagina/ - protocolo diferente


2. http://www.servidor.com.br/pagina/ - domnio diferente
3. http://servidor.com.br:81/pagina/ - porta diferente

Com "no pode acessar" queremos dizer que o cdigo Javascript da


pgina original no pode ler propriedades dessa segunda pgina, no
pode acessar elementos do DOM, nem fazer requisies Ajax para
ela.

Apresentando CORS
Pensando nisso, foram desenvolvidas tcnicas para que o
proprietrio de um site permita acesso a determinadas URLs ou
dentro de seu site. Uma delas o uso de JSONp, apresentado no
ltimo captulo. Outra chamada de "Cross Origin Resource
Sharing" (Compartilhamento de Recursos entre Domnios) tambm
conhecida como CORS.

O uso de CORS consiste numa troca de cabealhos entre o navegador


e o servidor, usando o mtodo HTTP OPTIONS, antes da requisio
real com GET ou POST. Nessa requisio OPTIONS o navegador
envia um header "Origin" e espera por um header "Access-Control-
Allow-Origin" com o mesmo valor ou com o valor "*". Se o header
"Access-Control-Allow-Origin" contiver um dos valores esperados o
navegador em seguida faz a requisio GET ou POST.

No servidor
No servidor, basta enviar o header HTTP na requisio OPTIONS
com um dos valores vlidos. Tambm ideal que, caso a requisio
use o mtodo OPTIONS, voc no retorne nenhum dado no corpo da
mensagem HTTP. Para fazer isso, por exemplo, em PHP:
<?
header('Access-Control-Allow-Origin: *');
if (strtolower($_SERVER['REQUEST_METHOD']) == 'options'){
exit();
}
?>

No navegador
Nos navegadores que tem suporte a CORS no preciso fazer nada
(isso no emocionante?) Basta fazer uma requisio Ajax comum,
com XMLHttpRequest, que o navegador vai, ao detectar que a
conexo para outro domnio, enviar primeiro a requisio
OPTIONS com o header Origin e verificar o header Access-Control-
Allow-Origin. Assim, o cdigo para requisies Ajax que voc j
conhece vai funcionar perfeitamente. Significa tambm que as
requisies que voc faz com jQuery.get, jQuery.post e jQuery.ajax
tambm j vo funcionar.

O problema est nos navegadores sem suporte a CORS. Hoje CORS


suportado nos navegadores Firefox, Chrome e Safari. O Opera
prometeu suporte a CORS em sua prxima verso, a 12. E o Internet
Explorer, desde a verso 8, suporta CORS atravs de um outro
objeto, o XDomainRequest (caramba, como eles conseguem?)

Isso significa que, por enquanto, voc tem que tratar duas ou trs
situaes para usar CORS em seu site:

1. Navegadores atuais, com suporte a CORS


2. Internet Explorer, com XDomainRequest
3. Navegadores antigos, atravs de um proxy

Vou mostrar como tratar a primeira e a segunda situaes. Vamos


criar uma funo chamada CORS, que faz a requisio usando o
recurso que estiver disponvel no navegador:
function CORS(url, data, callback) {
if (jQuery.browser.msie && window.XDomainRequest) {

// Se Internet Explorer, usamos XDomainRequest


var xdr = new XDomainRequest()
var params = ''

// Se tem dados, post


if(data)
for (key in data)
params = params+'&'+key+'='+data[key]
xdr.open(params?"post":"get", url)

xdr.onload = function() {
callback(this.responseText, 'success')
}
xdr.send(params||null)
} else {
// Se no Internet Explorer, o cdigo que j
// temos vai funcionar
jQuery.ajax(url,{
data:data,
success:callback,
type:[data?'POST':'GET']
})
}
}

Com o cdigo acima, para fazer requisio a uma URL com suporte a
CORS basta:
CORS('http://outroservidor.com.br/',null,function(t){
alert('Dados recebidos: '+t)
})

Suporte CORS. Com cuidado, mas suporte.


Ao publicar um servio ou API web, por favor, onde for possvel,
suporte CORS. Isso abre muitas possibilidades para as pessoas que
vo usar sua API ou consumir seu servio. Apenas, por favor, no
coloque indiscriminadamente em seu site o header Access-Control-
Allow-Origin: *

Escolha com cuidado as URLs que tero suporte a CORS. Como


regra geral, qualquer URL que esteja disponvel num webservice no
autenticado pode ter suporte a CORS sem que isso seja um
problema de segurana. Por outro lado, qualquer URL que contenha
dados privados do usurio no deve estar disponvel via CORS.

Se, por exemplo, voc colocar o header com valor "*" na pgina de
edio de perfil, onde h um formulrio com o endereo e telefone
do usurio, um agressor pode publicar uma pgina que requisita
essa pgina de edio de perfil, l os dados e os envia para o prprio
agressor.

No Wiki do W3C h uma pgina, CORS Enabled, com exemplos de


como configurar os headers para vrios servidores e linguagens de
programao diferentes.
Tratamento de Erro

Nem sempre (ou quase nunca) possvel prever o fluxo de um


programa em todas as circunstncias e garantir que tudo vai sair
bem. Imagine a seguinte funo (bem simples, por sinal):
function marca(id){
document.getElementById(id).className+=' destaque'
}

Ela simplesmente acrescenta a classe destaque ao elemento cujo id


ela recebeu. Agora imagine que voc constri uma outra funo, para
dar destaque a um determinado conjunto de elementos:
function destaque(visao){
var visoes={
'programar':['codigo','console','compila'],
'debugar':['codigo','console','inspetor','breakpoints'],
'design':['preview','wysiwyg','toolbar']
}
var itens=visoes[visao]
for(var i=0;i<itens.length;i++)
marca(itens[i])
}

Assim ao chamar destaque('design') os elementos #preview,


#wysiwyg e #toolbar vo ganhar a classe destaque. Agora, imagine
que em algumas telas ns temos o elemento #preview, em outras
no. O que vai acontecer quando essa funo for chamada numa tela
sem o elemento #preview? Um erro de Javascript:

H dois problemas com erros de Javascript. O primeiro deles que


em alguns navegadores os erros so apresentados na tela para o
usurio quando ocorrem, ou sinalizados na barra de status. E os
usurios no parecem ficar muito felizes com isso. O segundo
problema que os erros interrompem a execuo do script.

Porm, h uma maneira de lidar com isso, tratando os erros. A


primeira coisa que voc deve fazer entender onde os erros esto
ocorrendo e qual sua importncia, para decidir o que fazer com eles.
No nosso caso queremos que, se um dos elementos a ser destacado
no existir na tela atual, o script simplesmente continue. Para fazer
isso, vamos modificar a funo destaque, assim:
function marca(id){
try{
document.getElementById(id).className+=' destaque'
}catch(e){
// Aqui poramos qualquer cdigo que quisssemos que
// fosse executado em caso de erro. No caso particular
// desta funo, nada.
}
}

E pronto, os erros no vo mais nos incomodar. claro que voc no


pode simplesmente ignorar todos os erros, como fizemos com esse.
Digamos que queiramos tomar uma atitude em caso de erro. No
nosso caso, avisar o usurio:
function marca(id){
try{
document.getElementById(id).className+=' destaque'
}catch(e){
alert("VISO INCOMPLETA\nA viso que voc escolheu "+
"precisa do componente "+id+" que no est "+
"disponvel nessa tela.\nNo se preocupe, voc "+
"pode continuar usando o sistema normalmente. "+
"Se precisar contatar o suporte, informe o "+
"seguinte cdigo de erro:\n" + e.message)
}
}

E seu usurio ver:

Criando seus prprios erros


Imagine por exemplo que voc est desenvolvendo um simples
contador:
var contador=0
function acrescenta(n){
contador+=n
}

de se esperar que a funo acrescenta receba um nmero. Mas


voc pode se enganar e passar a ela um outro tipo de dado qualquer.
Uma string, por exemplo. Imagine que, por engano, voc faa:
acrescenta(2)
acrescenta("5") // Enviamos uma string por engano
acrescenta(2)
acrescenta(7)
acrescenta(17)
acrescenta(44)
alert(contador) // Mostra 25271744

O problema com esse tipo de erro que ele muito difcil de


debugar. Geralmente, as diversas chamadas sua funo no vo
estar assim, alinhadas uma embaixo da outra (e com um comentrio
indicando qual o erro!) Vo estar espalhadas por seu sistema, talvez
em arquivos diferentes. E no vo passar como parmetros valores
literais, como no exemplo acima, mas variveis cuja origem voc vai
ter que inspecionar.

Nesse caso, nosso problema no que o navegador est mostrando


um erro ao usurio. justamente o oposto. O fato de o navegador
silenciosamente receber nossa string e continuar trabalhando como
se estivesse tudo bem o nosso problema. No, no est tudo bem.
Tem uma string sujando nossa linda srie de nmeros e as
consequncias viro.

Eu resolveria esse problema assim:


var contador=0
function acrescenta(n){
if(typeof n!="number")
throw new Error("Argumento precisa ser um nmero: "+JSON.stringify(n))
contador+=n
}

Isso vai evitar que a funo receba uma string ou seja l o que for.
Claro, vai mostrar um erro caso a funo receba o argumento
errado. Mas o que voc prefere, um programa que lhe mostre um
erro e no faa o que voc precisa, ou um que parece funcionar mas
lhe faz pagar o dobro de imposto de renda?

Voc pode lanar qualquer coisa


Veja este trecho do console do Chrome:

Como voc pode ver, no preciso instanciar um objeto da classe


Error para lanar um erro. Voc pode passar para throw
absolutamente qualquer coisa. Isso pode ser muito til. Veja como
poderia ser nosso exemplo anterior:
var contador=0
function acrescenta(n){
if(typeof n!="number")
throw {
message: "Argumento precisa ser um nmero: "+JSON.stringify(n),
toString: function(){return this.message},
arguments: [n]
}
contador+=n
}

Isso vai ficar assim no console de erros:


E, ao encontrar a parte do cdigo onde essa funo foi chamada, voc
pode debugar fazendo coisas como:
try{
acrescenta(v)
}catch(e){
console.log('Argumentos passados: ',e.arguments)
throw e
}

E tendo o seguinte resultado:


Tipagem dinmica e Duck Typing

Para este captulo e os seguintes, vamos fazer um exerccio


comparando Javascript com outra linguagem, Python. Se voc no
entende nada de Python, no se preocupe, voc pode acompanhar
tudo o que vem a seguir olhando apenas os exemplos de Javascript.
Embora eu recomende que voc d uma espiadinha em Python.
Quem sabe a curiosidade no o faz aprender uma linguagem nova?
J se voc programador Python, vai achar a comparao muito
instrutiva.

Escolhi essas duas pelos poderosos recursos para se trabalhar com


funes. No qualquer linguagem que permite fazer o que elas
fazem. Os truques que vamos mostrar aqui, por exemplo, no podem
ser reproduzidos em PHP, Java ou C#.
Python
Python de longe minha linguagem de programao predileta. Open
source, desenvolvida por um desenvolvedor para deseonvolvedores,
voc pode usar Python nos mais diversos ambientes. Se voc usar
Linux ou Mac, provavelmente j tem Python instalada. Se usa
Windows, pode baix-la gratuitamente. Python o motor por trs
dos poderosos frameworks de desenvolvimento web Django,
TurboGears e web.py.

Javascript
Javascript uma linguagem a frente de seu tempo, e minha
segunda linguagem de programao predileta. Lembre-se, usvamos
Javascript em navegadores web 1995, e j naquela poca a linguagem
tinha recursos que hoje no se vem em muitas linguagens
consideradas mais robustas e poderosas. No se intimide com a
idia de que linguagens de scripts devem ser simples e pouco
sofisticadas, voc vai se surpreender com o poderoso modelo de
objetos super dinmicos do Javascript.

Ferramentas
Cada um dos exemplos que escrevermos estar em Python e em
Javascript. Voc pode escolher seguir acompanhando os exemplos
em apenas uma linguagem, ou em ambas. Se escolher seguir em
Python, tudo o que voc vai precisar do Python instalado e de um
bom editor de cdigo. Se for acompanhar em Javascript, vai precisar
de um editor de cdigo e do navegador Firefox com a extenso
Firebug.

Sobre tipagem
Ambas as linguagens que escolhemos para trabalhar usam um
modelo de tipagem dinmica. Mas mais do que isso, ambas usam
um modelo em que tudo um objeto, em que no h propriedades e
mtodos privados e tudo pode ser acessado em tempo de execuo.
Voc vai entender, acompanhando nosso exemplos, como isso uma
vantagem. Se voc tem trabalhado nos ltimos anos com Java, algum
sabor de C, ou uma linguagem de algumas geraes atrs como
Pascal ou Cobol, a experincia vai ser um pouco assustadora. Confie
em mim e resista ao impulso de sair correndo desesperadamente,
arrancar os seus cabelos ou telefonar para seu antigo professor da
faculdade. Seu autocontrole vai ser recompensado, eu prometo.

Em relao tipagem, as linguagens de programao podem ser


classificadas de duas maneiras:
Tipagem dinmica ou esttica
Isso tem a ver com o fato de voc precisar ou no declarar suas
variveis. Numa linguagem de tipagem dinmica, voc pode fazer
coisas como:
x=23
y="hello world"

Ou seja, voc no precisa declarar suas variveis, elas so criadas


automaticamente ao inicializ-las. Numa linguagem de tipagem
esttica, voc precisaria fazer:
int x=23;
string y="hello world";

Ou ainda:
int x;
string y;

x=23;
y="hello world";

E ainda, voc no poderia atribuir a x um valor que no fosse um


inteiro, nem a y um valor que no fosse uma string. Tanto Javascript
quanto Python trabalham com tipagem dinmica, ou seja, voc pode
criar as suas variveis a qualquer momento, e pode colocar um valor
de qualquer tipo nelas.

Tipagem forte ou fraca


Outra classificao diz respeito ao fato de a linguagem fazer ou no
converso de tipos (casting) automtica. Por exemplo, a seguinte
expresso:
"2"+2

Vai ser avaliada como 4 em PHP e "22" em Javascript. Porque


ambas fazem casting automtico, e o mecanismo de casting
diferente em ambas. Em PHP, sempre que voc usa o operador + os
valores sero convertidos para nmero. Em Javascript, sempre que
voc soma alguma coisa com uma string, feita uma concatenao
de strings. Javascript e PHP tm tipagem fraca. Em Python, essa
expresso gera o seguinte erro:
>>> "2"+2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects

Ou seja, voc precisa converter ambos os valores para nmeros se


quer uma soma aritmtica, ou para string se quer uma concatenao.
Python tem tipagem forte.
Funes como objetos (e porque
importa)

Funes so a ferramenta bsica de organizao de cdigo da


programao estruturada, so os tijolos da construo de quase todo
o software dos ltimos 20 anos. Chamadas de subrotinas ou
procedimentos, quando no retornam valor, chamadas de mtodos
quando pertencem a um objeto, as funes so onipresentes no
desenvolvimento de software hoje. Funes basicamente contm
cdigo, certo? Evitam que voc precise copiar e colar trechos de
cdigo comuns, bastando executar a funo quando voc quiser. Mas
algumas linguagens de programao orientadas a objeto nos
permitem fazer mais com as funes. Para essas linguagens, funes
so objetos com os quais voc pode trabalhar.
Funes so objetos
Funes no so apenas repositrios de cdigo. So objetos, com
mtodos e propriedades prprios. Pelo menos, nas linguagens que
escolhemos. Vamos comear criando uma funo muito simples:

Python:

def soma(a,b):
return a+b

Javascript:

function soma(a,b){
return a+b
}

No shell interativo do Python, ou no console da Firebug, aps criar


essa funo, execute: `dir(soma)` e voc vai ver uma listagem de
mtodos e propriedades do objeto. Na Firebug, infelizmente, a
listagem um tanto limitada, mostrando apenas a propriedade
prototype, mas a funo tem outros membros que vamos estudar.

Funes como valores


Agora, ainda com aquela nossa funo soma, tente o seguinte (no
Python ou no Javascript, tanto faz):
funcao2=soma
funcao2(3,5)

Em ambos os casos esse cdigo deve retornar 8. Conforme voc


pode ver, a funo soma na verdade se torna uma varivel soma, que
contm um objeto funo. Esse valor, esse objeto, pode ser atribudo
a outra varivel, em nosso caso funcao2. E essa nova varivel pode
ser executada. Isso pode ser bastante til. Veja esse exemplo:

Python:

def soma(a,b):
return a+b

def diferenca(a,b):
return a-b

def produto(a,b):
return a*b

operacoes={
"soma":soma,
"diferenca":diferenca,
"produto":produto
}

def executa(operacao,a,b):
return operacoes[operacao](a,b)

Javascript:

function soma(a,b){
return a+b
}

function diferenca(a,b){
return a-b
}

function produto(a,b){
return a*b
}

operacoes={
"soma":soma,
"diferenca":diferenca,
"produto":produto
}

function executa(operacao,a,b){
return operacoes[operacao](a,b)
}

Em ambos os casos, temos um conjunto de funes, soma, diferenca


e produto, e uma funo chamada executa que, baseada numa string
operacao, define que funo ser executada sobre os valores a e b.
Naturalmente, a funo executa poderia ser escrita assim:

Python:

def executa(operacao,a,b):
if operacao=="soma":
return soma(a,b)
if operacao=="diferenca":
return diferenca(a,b)
if operacao=="produto":
return produto(a,b)

Javascript:

function executa(operacao,a,b){
if(operacao=="soma")
return soma(a,b)
if(operacao=="diferenca")
return diferenca(a,b)
if(operacao=="produto")
return produto(a,b)
}

Mas voc percebe que esse ltimo jeito muito menos flexvel, uma
vez que eu tenho que ter um if para cada operao (no, um switch
em Javascript no resolveria bem o problema, pois eu continuaria
precisando de um case para cada funo.) Usar as funes como
valores para construir um array (ou dicionrio, no Python) torna
minha sintaxe mais simples e meu cdigo mais flexvel.
Mtodos Dinmicos

Como voc j percebeu, os objetos no Javascript so dinmicos e


tm propriedades dinmicas. Assim, voc pode dar novas
propriedades a qualquer objeto, quando quiser. Mesmo a objetos
nativos do Javascript:
var d=new Date()
d.y=44
alert(d.y) // Mostra 44

Logo, podemos tambm criar mtodos, dando ao objeto


propriedades cujo valor so funes. Assim:
function daterepr(){
return this.getFullYear()+'-'+
('0'+(this.getMonth()+1)).substr(-2)+
'-'+('0'+this.getDate()).substr(-2)
}

d=new Date()
d.repr=daterepr

alert(d.repr())

Voc tambm pode sobrescrever mtodos nativos dos objetos. Por


exemplo, o mtodo getYear do objeto Date deve, segundo a
especificao, retonar quantos anos se passaram desde 1900. Isso
era bastante til no sculo passado. Em 1999 esse mtodo retornava
99, o que era muito til para mostrar datas com anos de dois dgitos.
Hoje (2012) esse mtodo retorna 112, nmero para o qual eu no
vejo nenhuma utilidade prtica. Se voc quiser que um objeto Date
retorne os dois ltimos dgitos do ano no mtodo getYear, pode
fazer:
function getShortYear(){
return this.getFullYear()%100
}

d=new Date()
d.getYear=getShortYear
alert(d.getYear()) // Mostra 12 (agora em 2012, se voc
// est lendo isso em algum momento
// no futuro, isso deve mostrar os
// dois ltimos dgitos do ano atual.
Funes que recebem funes

Outro truque muito poderoso que voc pode fazer com funes
us-las como parmetros para outras funes. Com isso voc pode
automatizar parte de um algoritmo, mantendo outra parte flexvel.
Por exemplo, muito comum iterar pelos itens de um array, assim:

Python:

numeros=[1,2,3,4]
for numero in numeros:
print "O quadrado de %i %i" % (numero,numero**2)

Javascript:

numeros=[1,2,3,4]
for(var i=0;i<numeros.length;i++)
alert("O quadrado de "+numeros[i]+" "+Math.pow(numeros[i],2))

Aqui iteramos pelos itens do array, imprimindo o quadrado de cada


um. Uma parte desse algoritmo, iterar pelo array, sempre igual. A
outra parte, calcular o quadrado, ser diferente em cada situao em
tivermos que iterar pelo array. Poderamos ento criar uma funo
de varrer arrays, que recebe como parmetro a funo que ser
executada para cada elemento do array. Assim:

Python:

def paraCada(arr,fn):
for i in arr:
fn(i)

def imprimeQuadrado(numero):
print "O quadrado de %i %i" % (numero,numero**2)

numeros=[1,2,3,4]
paraCada(numeros,imprimeQuadrado)

Javascript:

function paraCada(arr,fn){
for(var i=0;i<arr.length;i++)
fn(arr)
}

function imprimeQuadrado(numero){
alert("O quadrado de "+numero+" "+Math.pow(numero,2))
}

numeros=[1,2,3,4]
paraCada(numeros,imprimeQuadrado)

Aqui, como voc deve notar, a funo paraCada recebe como


parmetro uma funo (fn). Voc pode usar esse truque sempre que
precisar reutilizar apenas parte de um algoritmo. Aqui, a funo
paraCada contm o algoritmo necessrio para interar por um array.
E recebe como parmetro uma funo que contm o algoritmo que
ser executado em cada interao dessas. Se voc quiser interar por
um array mas, ao invs de imprimir os quadrados, gerar um lista
HTML ou calcular as fatoriais, poder usar a funo paraCada
fornecendo a ela uma funo apropriada para as interaes.

Claro, o exemplo acima existe para fins didticos. Voc no


escreveria uma funo para algo to simples como interar por um
array, porque os desenvolvedores do Python e do Javascript,
sabendo que varrer arrays uma tarefa to comum em
programao, fizeram com que essas tarefas fossem fceis usando a
sintaxe comum da linguagem. Mas se o algoritmo for mais
complexo, encapsular a parte reaproveitvel dele numa funo pode
fazer todo o sentido. Tanto sentido, que os prprios criadores das
linguagens fizeram isso vrias vezes. Por exemplo, no algoritmo de
ordenao de arrays.

Ordenando Arrays
Veja o cdigo a seguir:

Python:

x=["2","40","7","12","3","6","80","55"]
x.sort()
print x

Javascript:

x=["2","40","7","12","3","6","80","55"]
x.sort()
alert(x)

Ambos os cdigos devem retornar o Array nessa ordem:


12, 2, 3, 40, 55, 6, 7, 80

O 12 vem antes do 2 e o 55 antes do 6, porque a ordenao de strings


feita em ordem alfabtica. Mas voc pode mudar esse
comportamento. Esse um claro exemplo em que uma funo
contm parte de um algoritmo, e recebe outra parte como
parmetro. O mtodo de arrays sort ordena o array, e se voc no
fornecer um algoritmo, ele vai ordenar strings alfabticamente, e
nmeros numericamente. Mas voc pode mudar esse
comportamento passando para ele como parmetro o algoritmo de
comparao dos elementos. O mtodo sort tem um importante
algoritmo de ordenao dentro dele, chamado bubble sorting. um
trabalho muito especializado, e voc no vai querer mexer nesse
algoritmo. Mas para ordenar os elementos, ele precisa compar-los,
e ele deixa voc dizer como os elementos sero comparados.

Voc faz isso passando para o mtodo sort uma funo. Essa funo
recebe dois parmetros que so os dois elementos que sero
comparados. Ela deve retornar um nmero positivo se o primeiro
elemento for maior que o segundo, um nmero negativo se for
menor, e zero se os nmeros forem iguais. Veja, por exemplo, como
ordenar os arrays de strings acima convertendo primeiro os
elementos para nmeros:

Python:

def comparaNumeros(a,b):
return int(a)-int(b)

x=["2","40","7","12","3","6","80","55"]
x.sort(comparaNumeros)
print x

Javascript:

function comparaNumeros(a,b){
return Number(a)-Number(b)
}

x=["2","40","7","12","3","6","80","55"]
x.sort(comparaNumeros)
alert(x)

Trabalhando assim, os desenvolvedores da funo sort conseguiram


implementar uma ordenao muito flexvel. Voc pode ordenar os
elementos do array por qualquer critrio que quiser, basta fornecer
o algoritmo de comparao apropriado.
Funes annimas

to comum criar funes com pequenos algoritmos como o de


ordenao acima que os desenvolvedores do Javascript e do Python
criaram meios para que voc possa inserir seus algoritmos sem criar
uma funo nomeada para isso. So as funes annimas. As funes
annimas so criadas no lugar onde so chamadas, simplificando o
cdigo. Por outro lado, se sua funo for complexa, voc vai preferir
escrever uma funo nomeada para isso. Em Python, inclusive, as
funes annimas tm uma sria restrio, voc s pode executar
uma operao de lgebra computacional, no pode escrever
condies e loops dentro delas. Veja as sintaxes:

Python:
x=["2","40","7","12","3","6","80","55"]
x.sort(lambda a,b:int(a)-int(b))
print x

Convm notar a sintaxe das funes annimas em Python, criadas


com a palavra chave lambda. Note que no h um comando return na
funo lambda. Depois dos dois-pontos vem uma expresso que ser
automaticamente retornada pela funo. Sem estruturas de controle,
sem laos, apenas uma expresso.

Javascript:

x=["2","40","7","12","3","6","80","55"]
x.sort(function(a,b){return Number(a)-Number(b)})
alert(x)

J em Javascript, funes annimas so funes comuns. Diferente


de Python, podem conter mais de uma expresso, condicionais,
loops e tudo o mais.
Parmetros flexveis

ainda possvel criar funes que recebam qualquer quantidade de


parmetros. Em javascript, isso feito usando a varivel especial
arguments. Veja:
function soma(){
var total=0
for(var i=0;i<arguments.length;i++)
total+=arguments[i]
return total
}

No declaramos os parmetros da funo e, dentro dela, lemos os


parmetros que forem passados, seja qual for sua quantidade, lendo
o array arguments. Veja um exemplo:
alert(soma(22,44,56,78,1,101,900,1))

Isso deve exibir 999.

Em Python, fazemos a mesma coisa declarando um nico argumento,


comeando com o caracter *, assim:
def soma(*a):
total=0
for i in soma:
total+=i
return total

Veja como usar nossa funo:


print soma(22,44,56,78,1,101,900,1)

Isso tambm deve exibir 999. (Claro, voc no vai construir uma
funo de soma assim em Python, porque j existe uma nativa, a
funo "sum"). H tambm como chamar uma funo, passando os
itens de um array como atributos de uma funo. Em Javascript,
usa-se o mtodo apply. Digamos que voc tenha um array com trs
nmeros, e precise pass-los funo raizes, que recebe trs
nmeros como parmetros:
args=[12,33,45]
raizes.apply(null,args)

O primeiro parmetro, para o qual passamos null, pode ser um


objeto. Se for passado, a funo ser chamada como um mtodo
deste objeto. Para fazer o mesmo em Python, chamamos a funo
usando o operador * antes da lista de parmetros:
args=[12,33,45]
raizes(*args)
Controle de escopo

Um detalhe importante ao trabalhar com funes onde voc cria


suas variveis e onde elas so visveis. Esta de longe a dvida mais
comum de iniciantes e a maior origem de problemas, mesmo em
scripts de programadores experientes. O problema bsico : vamos
supor que voc crie uma varivel chamada, por exemplo, contador. O
que vai acontecer se outra pessoa criar um script para a mesma
pgina com uma varivel tambm chamada contador? Qualquer
coisa pode acontecer, e a menos provvel o seu script continuar
funcionando como deveria.

Por isso foi criado o conceito de escopo de variveis. Ao declarar


uma varivel dentro de uma funo, em Javascript, h duas maneiras
de faz-lo:
function teste1(){
// Declarada sem a palavra-chave var, a variavel1
// global.
variavel1 = 42
}

function teste2(){
// Declarada com a palavra-chave var, a variavel2
// local.
var variavel2 = 42
}

teste()
teste2()

alert(variavel1) // Mostra 42
alert(variavel2) // Erro, pois variavel2 undefined

Em Python toda varivel criada dentro de uma funo local. Para


criar variveis globais preciso faz-lo fora da funo, e depois
declar-la na funo com a palavra chave global ou usar o
dicionrio globals().
var1=0

def teste1():
# Instruimos a funo a usar var1 global
global var1
var1=42

def teste2():
# Criamos var2 direto no dicionrio globals()
globals()['var2']=42

def teste3():
# var3 local
var3=42

teste1()
teste2()
teste3()

print var1 # Mostra 42


print var2 # Mostra 42
print var3 # Erro, pois var3 no est definida

Agora, a regra de ouro da programao em relao ao escopo de


variveis:

Variveis globais so coisa do capeta.

Toda vez que voc foi obrigado a criar uma varivel global, havia
algo de errado com o projeto de suas funes e classes. No captulo
16 vamos estudar em detalhes como evitar por completo o uso de
variveis globais. At l, evite o quanto voc puder o uso de
variveis globais.

Funes so variveis
Um fato importante que, tanto em Python quanto em Javascript,
ao declarar uma funo voc est na verdade criando uma varivel
cujo valor a funo. Veja:
function teste(){
}

alert(teste) // Mostra uma representao da funo.

Ou em Python:
def teste():
pass

print teste # Mostra uma representao da funo

Isso significa que, da mesma forma que podemos ter problemas com
o nome de variveis, podemos ter problemas com o nome de
funes. Nesse caso, em Python o problema resolvido usando
mdulos. Se voc escreve suas funes em diferentes arquivos do
Python, elas esto em mdulos diferentes. Em Javascript no h
mdulos automticos, e voc vai aprender como escrever seu cdigo
em mdulos no captulo 16.
Funes internas

Voc pode declarar uma funo dentro de outra, e ela ter o mesmo
escopo de variveis que a funo pai. E, se voc retornar a funo
filha, o escopo da funo pai estar disponvel dentro dela. Veja esse
exemplo em Javascript:
function pai(){
var x=33
function filha(){
alert(x)
}
return filha
}
// Aqui executamos a funo pai, que retorna a
funo filha:
f=pai()
// Ao executarmos a funo filha, o escopo da funo pai ainda existe. Assim
// varivel x no morreu:
f()
// Isso vai mostrar "33".

E a mesma coisa em Python:


def pai():
x=33
def filha():
print x
return filha
# Aqui executamos a funo pai, que retorna a funo filha:
f=pai()
# Ao executarmos a funo filha, o escopo da funo pai ainda existe. Assim,
# varivel x no morreu:
f()
# Isso vai mostrar "33".
Decorators

Certifique-se de ter entendido bem as funes com nmero varivel


de parmetros e o escopo persistente nas funes internas antes de
prosseguir. Se tem dvidas, revise as duas ltimas sees at ter
certeza de ter entendido, voc vai precisar.

O truque agora consiste em escrever uma funo que recebe outra


como parmetro, e a modifica. Vamos examinar um exemplo,
digamos, uma funo que soma trs nmeros. Javascript:
function somaTres(a,b,c){
return a+b+c
}

Ou, em Python:
def somaTres(a,b,c):
return a+b+c

Em ambas as linguagens, essa funo pode ser chamada passando


strings, assim:
somaTres("12","5","2")

O resultado no ser 19, mas "1252", porque strings so


concatenadas pelo operador de soma. Digamos que voc tenha vrias
funes como essa, e precise que, ao receber strings, suas funes
convertam os valores para nmeros antes de fazer a soma. Voc
poderia modificar suas funes uma a uma, mas isso levaria um
bocado de tempo. Voc pode ento criar um modificador de funes,
um "decorator", assim (Javascript):
function numberfy(fn){
return function(){
for(var i=0;i<arguments.length;i++)
arguments[i]=Number(arguments[i])
return fn.apply(null,arguments)
}
}

Ou, em Python:
def numberfy(fn):
return lambda *args:fn(*map(float,args))

Note que ambas as funes retornam funes. Podemos ento fazer,


em ambas as linguagens:
novoSomador=numberfy(somaTres)
novoSomador("12","5","2")

A funo numberfy recebe somaTres como o parmetro fn. Essa


varivel fn no morre quando numberfy termina, porque ela retorna
uma nova funo. Essa nova funo converte todos os valores para
nmeros antes de chamar fn. Como guardamos a nova funo na
varivel novoSomador, ela passa a ser uma funo que chama
numberfy, convertendo primeiro os valores. Mas a parte mais
interessante que podemos guardar nova funo na varivel
somaTres. Assim, ela substituir a antiga funo somaTres. como
se a estivssemos modificando, assim:
somaTres=numberfy(somaTres)
somaTres("12","5","2")

Digamos que voc tenha uma srie de funes que precisam ser
transformadas assim, para ter seus parmetros convertidos para
nmeros. Depois de ter criado a numberfy, bastar fazer:
funcaoUm=numberfy(funcaoUm)
funcaoDois=numberfy(funcaoDois)
funcaoTres=numberfy(funcaoTres)
funcaoQuatro=numberfy(funcaoQuatro)

Decorators e Python
O uso de decorator to poderoso e to comum em Python, que
existe uma construo especfica da linguagem para facilitar sua
aplicao. Digamos que voc queira aplicar o decorator numberfy
funo somaTres. Basta fazer o seguinte ao declarar a funo:
@numberfy
def somaTres(a,b,c):
return a+b+c

Ao declarar uma funo precedida por um decorator com arroba, ela


ser automaticamente modificada pelo decorator. Essa sintaxe
facilita muito a leitura. Por exemplo, ao ler:
@log
def somaTres(a,b,c):
return a+b+c

Voc entender que todas as chamadas funo sero logadas.

Exemplos de uso
Voc pode ter, por exemplo, um decorator para log do sistema. Ao
modificar uma funo com esse decorator, todas as chamadas a ela
sero logadas. Pode ter um decorator para funes que s podem ser
chamadas se o usurio estiver logado. Cada vez que forem
executadas, ser primeiro verificado se o usurio est logado. Outro
exemplo comum de uso a adio de um contexto a um mtodo
dinmico.

H muitas situaes em que voc gostaria que um mtodo dinmico


recebesse parmetros extras, mas voc no pode modificar a
chamada ao mtodo dentro do objeto que o recebe. Por exemplo, ao
atribuir eventos a uma lista de objetos. Veja esse trecho de
Javascript:
for(var i=0;i<obj.length;i++){
obj[i].onclick=clicado
}

Ou a mesma coisa em Python:


for i in range(len(obj)):
obj[i].onclick=clicado

Note que estamos fazendo um loop for para atribuir o evento onclick
a todos os objetos. Digamos que a assinatura do evento onclick
nesses objetos seja exatamente como a assinatura do evento no
DOM HTML, ou seja, voc vai receber apenas um objeto
representando o evento. E se voc quisesse que a funo "clicado"
recebesse tambm o ndice do objeto que sofre o clique, no nosso
caso, a varivel i? Bastaria criar um decorator que guardasse i em
seu escopo. Veja em Javascript:
function clickval(fn,i){
return function(e){
return fn(e,i)
}
}

E em Python:
def clickval(fn,i):
return lambda self,e:fn(self,e,i)

Assim, na hora de atribuir o evento, faramos, em Javascript:


for(var i=0;i<obj.length;i++){
obj[i].onclick=clickval(clicado,i)
}

Ou em Python:
for i in range(len(obj)):
obj[i].onclick=clickval(clicado,i)
Orientao a Objeto

Neste captulo vamos dar uma olhada na sintaxe para os conceitos


bsicos de Orientao a Objeto em javascript. Este conhecimento
essencial para que voc possa escrever scripts realmente
reaproveitveis. Estou supondo que voc j conhece os conceitos
bsicos de Orientao a Objeto, por isso vamos nos preocupar em
como implement-los no javascript. Alis, a experincia deve faz-lo
pensar um pouco sobre o que realmente Orientao a Objeto.

Linguagens de programao so abstraes. Todos os conceitos de


uma linguagem de programao, variveis, operadores, funes,
mdulos, objetos, nada disso existe de verdade. Tudo o que o
computador realmente tem so registradores, uma pilha de
memria e um conjunto de instrues. Imagine ento que voc tem
uma linguagem de programao dinmica, mas sem classes e
objetos. Como voc implementaria classes e objetos?

Note que, em Javascript, podemos criar um objeto de diversas


maneiras. Veja:
// Um jeito
var joaquim=new Object()
joaquim.nome="Joaquim Silva"
joaquim.email="jsilva@fbi.gov"

// Outro jeito
var joaquim={}
joaquim.nome="Joaquim Silva"
joaquim.email="jsilva@fbi.gov"

// Mais um jeito
var joaquim={
nome: "Joaquim Silva",
emai: "jsilva@fbi.gov"
}

Vamos comear criando uma classe. Para isso, por mais estranho
que isso possa parecer para quem vem de outras linguagens, usamos
a palavra chave function:
function Pessoa(nome,idade,email){
// Para criar as propriedades da classe usamos a palavra-chave this
this.nome=nome
this.idade=idade
this.email=email

// No existe, em javascript, o conceito de mtodo. Um mtodo uma


// propriedade como outra qualquer, cujo valor uma funo e,
// portanto, executvel.
this.digaOi=function(){
alert("Oi, eu sou "+this.nome+"!")
}

// Vamos testar, criando um objeto da classe pessoa:


joaquim=new Pessoa("Joaquim", 34, "jalmeida@cia.gov")
// E chamando o mtodo digaOi:
joaquim.digaOi()

Como voc pode ver, as diferenas entre uma funo comum e uma
classe so que dentro da classe ns definimos atributos usando a
palavra-chave this e quando chamamos esta classe, usamos a
palavra-chave new para criar um novo objeto. Ao cham-la com new,
o Javascript cria um novo objeto vazio na varivel especial this.
Ento podemos atribuir a this as propriedades que quisermos.

Herana
Para fazer herana, sejamos honestos, a sintaxe um tanto quanto
deselegante:
function Pessoa(nome,idade,email){
// Para criar as propriedades da classe usamos a palavra-chave this
this.nome=nome
this.idade=idade
this.email=email

// No existe, em javascript, o conceito de mtodo. Um mtodo uma


// propriedade como outra qualquer, cujo valor uma funo e,
// portanto, executvel.
this.digaOi=function(){
alert("Oi, eu sou "+this.nome+"!")
}

function Programador(nome,idade,email,loginCVS){
// Programador herda de Pessoa
this.Pessoa=Pessoa;this.Pessoa(nome,idade,email)

// Mas Programador tem algo que Pessoa no tem, login no CVS


this.loginCVS=loginCVS

// E Programadores no dizem oi como as Pessoas comuns


this.digaOi=function(){
alert("Ping!")
}

// Vamos testar, criando um objeto da classe Programador:


joaquim=new Programador("Joaquim", 34, "jalmeida@cia.gov", "almeida")
// E chamando o mtodo digaOi:
joaquim.digaOi()

Note bem como fazemos a herana. Primeiro ns criamos uma


propriedade para a classe filho cujo valor a prpria classe pai. Isso
porque no javascript no existe diferena entre classe e construtor.
A classe a prpria funo contrutora. Ento, o que estamos fazendo
dizer que o Programador tem um mtodo Pessoa, que o
construtor da classe Pessoa:
this.Pessoa=Pessoa

Em seguida executamos esse construtor. Isso vai dar ao


Programador tudo o que a Pessoa tem:
this.Pessoa(nome,idade,email)

Para no me confundir, como as duas linhas acima so na verdade


uma operao s, uma herana, costumo coloc-las na mesma linha,
separadas por ponto-e-vrgula:
this.Pessoa=Pessoa;this.Pessoa(nome,idade,email)

Est feita nossa herana. Naturalmente, como voc deve imaginar,


para trabalhar com herana mltipla basta incluir mais de uma linha
de herana.
Prototype

Voc notou, no captulo anterior, o uso da palavra chave new e da


varivel especial this. Se voc entendeu direitinho a natureza
dinmica dos objetos do Javascript, deve estar se perguntando o
porqu de tudo isso. No poderamos simplesmente resolver assim?
function Pessoa(nome,idade,email){
return {
nome:nome,
idade:idade,
email:email,
digaOi:function(){
alert("Oi, eu sou "+this.nome+"!")
}
}
}
// Vamos testar, criando um objeto da classe pessoa:
manoel=new Pessoa("Manoel", 27, "msouza@kgb.gov.ru")
// E chamando o mtodo digaOi:
manoel.digaOi()

Parece bem mais simples, no? Na verdade, isso no exatamente a


mesma coisa. Alm de no haver uma maneira simples de
implementar herana nesse caso, h ainda uma outra diferena.
Objetos gerados com new e this tm uma propriedade constructor
que aponta para a funo construtora. Assim, possvel fazer no
exemplo do captulo anterior:
joaquim.constructor==Pessoa // retorna true

Essa referncia ao construtor pode ser muito til. Alm disso, essa
referncia cria um vnculo dinmico entre o objeto e a classe.
Lembra-se de que no captulo anterior dissemos que quando voc
invoca uma funo com new como se o Javascript criasse um novo
objeto vazio e guardasse na varivel especial this? Bom, no
exatamente isso o que acontece. Na verdade o objeto this criado
baseado num prottipo. Cada funo tem seu prprio prottipo de
construo de objetos.

O interessante que voc pode alterar esse prottipo. Pode incluir e


modificar propriedades do prottipo. Por exemplo, voc pode fazer:
Programador.prototype.linguagens=['Javascript', 'Python', 'PHP', 'Ruby', 'Sm

E a partir desse momento todo programador, j criado ou que venha


a ser criado, ter uma propriedade linguagens com esse valor:
for(var i=0;i<joaquim.linguagens.length;i++)
alert(joaquim.linguagens[i])

isso mesmo que voc entendeu, voc pode modificar uma classe
depois de ter criado objetos, e todos os objetos j criados vo ser
modificados de uma vez s.

Modificando as classes nativas


Bom, voc pode achar que isso no muito til, e eu tendo a
concordar com voc. Mas voc pode usar essa mesma tcnica para
alterar as classes nativas do Javascript. Por exemplo:
// Ensina a todos os nmeros o mtodo moeda
Number.prototype.moeda=function(){
var f=Math.abs(Math.round(this*100))
var c=f%100
if(c<10)c='0'+c
f=Math.floor(f/100)+""
f=f.replace(/^(\d\d?)((\d{3})+)$/,'$1.$2')
.replace(/(\d{3})(?=\d)/g,'$1.')
return (this<0?'-':'')+f+','+c
}
// Agora podemos usar:
var x=-12304.709
alert(x.moeda()) // Mostra -12.304,71

Ou ainda:
// D aos arrays um mtodo shuffle, que os embaralha
Array.prototype.shuffle=function(){
for(var i=0;i<this.length;i++){
var s=Math.floor(Math.random()*this.length)
var temp=this[i]
this[i]=this[s]
this[s]=temp
}
}

y=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
y.shuffle()
alert(y)

Ou ainda:
// Ensinamos o Array a fazer max
Array.prototype.max=function(){
// Se tiver dificuldade com essa linha,
// reveja o captulo 10
return Math.max.apply(Math,this)
}

y=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
alert(y.max()) // Mostra 20

Assim, se voc sente falta de alguma coisa em Javascript, voc pode


criar. Coisas como um mtodo strftime para objetos Date ou printf
para strings podem ser criadas com facilidade.

Ensinando o mesmo truque a todos os objetos


Por fim, importante que voc saiba que em Javascript todos os
objetos herdam da classe base Object. Assim, ao inserir um mtodo
ou propriedade no prottipo de Object estamos ensinando esse
mtodo a todos os objetos. Por exemplo, voc poderia fazer:
Object.prototype.JSON=function(){
return JSON.stringify(this)
}

partir de ento qualquer objeto, de qualquer classe, no Javascript


ter um mtodo JSON:
x=1
alert(x.JSON())
x={nome:"Pedro",email:"pedro@interpol.gov.uk"}
alert(x.JSON())
x=new Date()
alert(x.JSON())
Modularizao

Uma das grandes lacunas do Javascript a falta de um mecanismo


de namespaces ou mdulos. Todas as funes que voc cria so
globais. Todas as variveis declaradas fora de uma funo so
globais. Sem algum trabalho para modularizar seu cdigo,
inevitvel chegar a um conflito de nomes algum dia.

Porm, com o que j aprendemos sobre objetos literais, funes


annimas e escopo, podemos com facilidade implementar um
mecanismo de namespaces.

Veja este exemplo:


function over(){
this.src=detalhes
}

function out(){
this.src=original
}

function troca(imgobj){
original=imgobj.src
detalhes=imgobj.src.replace(/\.png$/,'_detail.png')
imgobj.onmouseover=over
imgobj.onmouseout=out
}

troca(document.getElementById('img1'))

O cdigo bastante simples. Ele configura os eventos mouseover e


mouseout da imagem #img1 para acrescentar e remover "_detail" na
URL da imagem. Isso faz com que a imagem mude ao passar o
mouse. Mas h uma srie de problemas com esse cdigo:

1. H trs funes globais: over, out e troca. Se houver outro


script na mesma pgina com alguma funo com esses nomes,
teremos problemas.
2. H duas variveis globais, original e detalhes, que armazenam
a URL da imagem. Mesmo problema.
3. Como as variveis acima so globais, tambm no possvel
aplicar esse script a mais de uma imagem na mesma pgina.

Note que este problema a reduo de uma srie de problemas


comuns. Se voc precisa implementar um mecanismo de lightbox,
vai precisar de variveis para o contexto do lighbox, funes para
tratar os eventos e etc. Ser a mesma coisa se precisar implementar
galerias e carrossis, menus, validadores de formulrios, etc.

Podemos melhorar muito isso, assim:


function troca(imgobj){
var original=imgobj.src
var detalhes=imgobj.src.replace(/\.png$/,'_detail.png')
function over(){
this.src=detalhes
}
function out(){
this.src=original
}
imgobj.onmouseover=over
imgobj.onmouseout=out
}

// Agora o script pode ser usado para vrias imagens:


troca(document.getElementById('img1'))
troca(document.getElementById('img2'))

Agora s temos uma funo global, troca. As duas variveis agora


so locais e as funes out e over so internas, compartilhando do
escopo local. Com isso, resolvemos o problema de s poder executar
o script para uma imagem. Agora ele pode ser chamado quantas
vezes quisermos, porque cada execuo gera um novo escopo de
variveis. Alm disso, quase resolvemos os possveis problemas de
conflitos de nomes. Falta resolver um possvel conflito com o nome
da funo troca.

Aqui h mais de uma abordagem possvel. A primeira chega a ser


infantil, consistem em renomear a funo para algo como
workshopjs_troca. Isto , prefixamos a funo com o nome do script
ou aplicao que estamos desenvolvendo. O principal argumento
contra esta abordagem que o cdigo fica muito, muito feio.

Uma segunda abordagem criar objetos para serem nossos


namespaces. Algo assim:
workshopjs = workshopjs || {}

workshopjs.troca=function(imgobj){
var original=imgobj.src
var detalhes=imgobj.src.replace(/\.png$/,'_detail.png')
function over(){
this.src=detalhes
}
function out(){
this.src=original
}
imgobj.onmouseover=over
imgobj.onmouseout=out
}

// Agora o script pode ser usado para vrias imagens:


workshopjs.troca(document.getElementById('img1'))
workshopjs.troca(document.getElementById('img2'))

Com isso, ainda temos uma varivel global. Mas agora ela no
nossa funo, mas um namespace que criamos. Logo, seu nome
muito menos genrico. E se formos criar cinco scripts diferentes
nessa mesma aplicao, podemos coloc-los para usar o mesmo
namespace. exatamente o que voc faz quando cria um plugin de
jQuery, por exemplo. Todos os plugins so guardados em jQuery.fn e
encapsulam todas as funes e variveis de que precisam, de modo
que voc s precisa se preocupar se seu plugin no tem o mesmo
nome de outro plugin.

Outra abordagem bastante usada de fazer com seu script seja


autoexecutado, definindo regras de mark-up sobre as quais ele deve
agir. No nosso caso, poderamos definir que todas as imagens que
tivessem a classe "trocador" seriam automaticamente tratadas pelo
script. Nesse caso, colocar nosso script todo dentro de uma funo e
execut-lo:
function grandeFuncaoQueAcomodaTodoScript(){
function troca(imgobj){
var original=imgobj.src
var detalhes=imgobj.src.replace(/\.png$/,'_detail.png')
function over(){
this.src=detalhes
}
function out(){
this.src=original
}
imgobj.onmouseover=over
imgobj.onmouseout=out
}

// Agora o script pode ser usado para vrias imagens:


var imagens=document.getElementsByClassName('trocador')
for(var i=0;i<imagens.length;i++)
troca(imagens[i])
}

grandeFuncaoQueAcomodaTodoScript()

Parece que trocamos seis por meia dzia, no ? Agora temos a


funo grandeFuncaoQueAcomodaTodoScript como global. Mas,
com o conhecimento que temos de funes annimas, podemos
transform-la em uma, e execut-la:
(function(){
function troca(imgobj){
var original=imgobj.src
var detalhes=imgobj.src.replace(/\.png$/,'_detail.png')
function over(){
this.src=detalhes
}
function out(){
this.src=original
}
imgobj.onmouseover=over
imgobj.onmouseout=out
}

// Agora o script pode ser usado para vrias imagens:


var imagens=document.getElementsByClassName('trocador')
for(var i=0;i<imagens.length;i++)
troca(imagens[i])
}())

Tenho escolhido muito esta segunda abordagem para scripts que


devem interagir com o mark-up e no com outros scripts, como o
exemplo acima. Outros exemplos incluem validadores de
formulrio, menus, lighboxes, tooltips, carrossis, galerias, etc. J
quando meu script parte de uma lgica maior e mais complexa, e
precisa ser usado por outros scripts para ser til, tenho preferido a
abordagem de coloc-lo em um namespace.
Boas prticas com jQuery

comum, quando se fala em "boas prticas" em desenvolvimento de


software, que se despeje uma lista de recomendaes, muita vezes
desconexa uma da outra. tambm bastante comum que autores
diferentes apresentem conselhos exatamente opostos em relao ao
que consideram boas prticas. De maneira geral, isso acontece
porque antes de listar as boas prticas de desenvolvimento preciso
primeiro definir o que se considera bom.

Aqui h sempre um equilbrio difcil de ser encontrado entre


diversos fatores, e em Javascript no diferente. Os trs principais
fatores levados em conta quando falamos de boas prticas so:

1. Performance da aplicao, ou seja, quo rpido o cdigo


executa e quantos recursos da mquina ele consome.
2. Produtividade, ou seja, quo rpido a aplicao construda.
3. Acessibilidade e compatibilidade, o que tem a ver com
quantas pessoas vero seu site e em quantas situaes
diferentes ele poder ser usado.
4. Qualidade do cdigo, o terceiro fator, tambm no to
simples de definir. Para alguns tem a ver com a quantidade de
testes automatizados que voc tem. Para outros, com o design
de suas funes, classes e objetos. Outros valorizam a
quantidade de comentrios e a indentao e beleza do cdigo.

Tendo definies to diferentes, importante separarmos o que


chamamos de boas prticas em recomendaes para performance,
para produtividade e para qualidade do cdigo. claro que muitas
recomendaes vo servir a mais de um propsito, mas outras sero
completamente opostas caso seu foco seja um ou outro. E
importante que voc entenda que no h respostas certas quanto a
importncia de cada um desses aspectos. Voc deve avaliar a
situao de cada projeto para definir o que ser importante para
voc.

Prticas sobre performance


1. Diminua o tamanho dos seus scripts, assim com o tamanho
geral da pgina, incluindo imagens, CSS, flash e o prprio HTML. A
demora na carga inicial um dos principais fatores para a percepo
de lentido em sua aplicao por parte dos usurios. O que nos leva
segunda dica:

2. Carregue seus scripts depois do contedo, ou, pelo menos,


aqueles que no forem essenciais para o uso do contedo. Como
voc pode, dentro de seus scripts, executar document.write(), o
navegador pra a carga do restante do contedo ao encontrar uma
tag script e s continua o parsing do HTML aps o script ter sido
executado. Para evitar isso, h dois mtodos possveis.

O primeiro o uso do atributo defer:


<script src="scriptlongoepesado.js" defer></script>

Ou, na sintaxe XHTML:


<script src="scriptlongoepesado.js" defer="defer"></script>

O atributo defer funciona em qualquer navegador atual, exceto o


Opera. Ele indica ao navegador que esse script pode ser carregado
paralelamente ao carregamento da pgina. claro, documento.write
no estar disponvel no seu script (mas isso no um problema,
certo?)
Outra tcnica para fazer isso carregar dinamicamente seus scripts.
Se voc j inseriu Google Analytics numa pgina, deve ter visto
cdigo como esse:
var ga = document.createElement(script);
ga.type = text/javascript;
ga.async = true;
ga.src = (https: == document.location.protocol ?
https://ssl : http://www) + .google-analytics.com/ga.js;
var s = document.getElementsByTagName(script)[0];
s.parentNode.insertBefore(ga, s);

Esse cdigo cria um novo elemento script, configura seu atributos


src e async e o insere na pgina. Voc pode colocar este cdigo no
final de sua pgina, ou mesmo numa funo atribuda ao evento load.

Voc deve ter notado o atributo async. Alguns navegadores,


principalmente em celulares mas tambm algumas verses antigas
do Firefox, enfileram os scripts inseridos dinamicamente. Assim, se
voc inserir cinco scripts com essa tcnica e o primeiro for pesado
ou lento para carregar e executar, os outros ficaro esperando. O
atributo async resolve esse problema em boa parte desses
navegadores.

Uma observao importante: na dica 2 falei em carregar depois os


scripts "no essenciais para uso do contedo". Voc pode estar se
perguntando se o ideal no seria que todos os scripts fossem no
essenciais, e o contedo sempre pudesse ser acessado sem
javascript. Sim, esse seria o ideal, e para a maioria dos sites um
alvo bastante razovel. Mas h situaes em que isso no faz o
menor sentido. Pense em algum construindo um jogo com HTML5
Canvas, por exemplo.

3. Entenda o mecanismo de seletores da jQuery.


Internamente, os seletores jQuery usam as mesmas funes que
esto disponveis para voc: getElementById,
getElementsByTagName, querySelectorAll, etc... E qual a mais
rpida delas? Voc sabe, getElementById. Assim, se voc vai ter um
nico campo de busca em toda a pgina, colocar nele um id e
chamar:
$('#campodebusca')

muito mais rpido que fazer a mesma coisa usando uma classe:
$('.campodebusca')

4. Cacheie seus objetos e chamadas a funes. Por exemplo, veja o


cdigo a seguir:
var values=[]
for(var i=0;i<$('.destaque').length;i++){
values.push($($('.destaque')[i]).html())
}

Embora seja bastante simples, esse cdigo muito ruim. Digamos


que a pgina tenha dez objetos com a classe destaque. Quantas
chamadas funo $() sero feitas? Trinta. Agora veja esta verso:
var values=[]
var destaques=$('.destaque')
for(var i=0;i<destaques.length;i++){
values.push($(destaques[i]).html())
}

Agora teremos onze chamadas ao invs de trinta. Uma para criar a


varivel destaques mais uma para cada um dos destaques. Uma dica
que pode ajudar lembrar sempre que voc pode usar o bom e velho
DOM sempre que quiser:
var values=[]
var destaques=$('.destaque')
for(var i=0;i<destaques.length;i++){
values.push(destaques[i].innerHTML)
}

O cdigo acima faz exatamente a mesma coisa, com apenas uma


chamada funo $().

5. Aprenda jQuery. O cdigo acima pode ser substitudo por:


var values=$('destaques').map(function(){return this.innerHTML})

Assim como neste exemplo, em diversas situaes tenho visto


programadores, mesmo experientes, escrevendo coisas que a jQuery
j faz. Gaste tempo estudando suas ferramentas, vale a pena.

Outras dicas (produtividade, qualidade,


acessibilidade, etc.)
1. Adote um padro de codificao. No importa muito se voc
vai usar dois espaos, quatro espaos ou uma tabulao ao escrever
Javascript. Se vai colocar seus comentrios dentro ou fora da
declarao da funo ou se vai abrir chaves na linha da declarao ou
na linha de baixo. Existem bons argumentos para cada uma das
possveis respostas. Mas importante que, ao escolher um padro,
voc o mantenha. E se vai trabalhar em equipe, claro, vocs vo ter
que chegar a um consenso juntos.

2. Baseie seu cdigo em bom mark-up e corte as opes. A


maioria dos plugins de jQuery genrica demais. Voc pode olhar a
documentao de um plugin e ver a quantidade de opes que ele
possui, e achar que isso bom. Muitas vezes, bom mesmo. Mas
lembre-se de que cada opo do plugin significa mais cdigo no
plugin, mais complexidade e mais chance de erro. No use o atual
ecossistema de plugins como uma norma para a construo dos seus.

Por exemplo, digamos que voc queira escrever um plugin que far o
preload de determinados links num iframe oculto, de modo que
quando o usurio clicar num desses links o navegador responda mais
rpido. Voc poderia fazer seu plugin com uma assinatura assim:
// order -> Ordem de carregamento
// attr -> Atributo da url a carregar (valor padro: href)
// iframeparent -> Elemento onde sero criados os iframes
// de preloading. Se nada for passado, ser
// criado um novo elemento, oculto.
jQuery.fn.preload=function(order,attr,iframeparent){
// Aqui vai o cdigo do plugin, que por sua conta ;-)
}

Muitos plugins ainda recebem um objeto de configurao ao invs de


uma lista de parmetros. Isso bom, e se voc for escrever um
plugin como o acima, sugiro que troque a assinatura para receber
um nico parmetro, o objeto de configurao.

Porm, eu resolveria este problema sem um plugin, com um cdigo


assim:
$(window).load(function(){
$('a.preload').each(function(){
// Aqui fazemos o preload.
})
})

Nenhuma opo, nenhuma configurao. Basta colocar nos seus links


a classe preload e pronto. Isso pode no cobrir alguma necessidade
muito especfica de algum, que um dia pode ter uma situao em
que no seja possvel incluir a classe preload em seus links. Voc
quer mesmo carregar essa complexidade toda por causa de uma
situao hipottica que talvez um dia acontea?

Agora d uma olhada na documentao dos seus plugins prediletos.


Tenho certeza que voc vai encontrar muita complexidade
desnecessria.

3. Deixe o CSS fazer o trabalho dele. Vamos construir um menu


em rvore como exemplo. Vamos comear com a pergunta mais
importante, como representar um menu em rvore com HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="jquery.js"></script>
<script src="tree.js"></script>
<link rel="stylesheet" href="tree.css" />
</head>
<body>
<ul class="tree">
<li>
<a href="#">Link 1</a>
<ul>
<li>
<a href="#">Link 1.1</a>
<ul>
<li><a href="#">Link 1.1.1</a></li>
<li><a href="#">Link 1.1.2</a></li>
<li><a href="#">Link 1.1.3</a></li>
</ul>
</li>
<li>
<a href="#">Link 1.2</a>
<ul>
<li><a href="#">Link 1.2.1</a></li>
<li><a href="#">Link 1.2.2</a></li>
<li><a href="#">Link 1.2.3</a></li>
</ul>
</li>
<li>
<a href="#">Link 1.3</a>
<ul>
<li><a href="#">Link 1.3.1</a></li>
<li><a href="#">Link 1.3.2</a></li>
<li><a href="#">Link 1.3.3</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Link 2</a>
<ul>
<li>
<a href="#">Link 2.1</a>
<ul>
<li><a href="#">Link 2.1.1</a></li>
<li><a href="#">Link 2.1.2</a></li>
<li><a href="#">Link 2.1.3</a></li>
</ul>
</li>
<li>
<a href="#">Link 2.2</a>
<ul>
<li><a href="#">Link 2.2.1</a></li>
<li><a href="#">Link 2.2.2</a></li>
<li><a href="#">Link 2.2.3</a></li>
</ul>
</li>
<li>
<a href="#">Link 2.3</a>
<ul>
<li><a href="#">Link 2.3.1</a></li>
<li><a href="#">Link 2.3.2</a></li>
<li><a href="#">Link 2.3.3</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Link 3</a>
<ul>
<li>
<a href="#">Link 3.1</a>
<ul>
<li><a href="#">Link 3.1.1</a></li>
<li><a href="#">Link 3.1.2</a></li>
<li><a href="#">Link 3.1.3</a></li>
</ul>
</li>
<li>
<a href="#">Link 3.2</a>
<ul>
<li><a href="#">Link 3.2.1</a></li>
<li><a href="#">Link 3.2.2</a></li>
<li><a href="#">Link 3.2.3</a></li>
</ul>
</li>
<li>
<a href="#">Link 3.3</a>
<ul>
<li><a href="#">Link 3.3.1</a></li>
<li><a href="#">Link 3.3.2</a></li>
<li><a href="#">Link 3.3.3</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</body>
</html>

Listagem do arquivo exemplo 17.01

E isso aparece assim:


O que prefeitamente aceitvel em navegadores sem Javascript. A
pergunta que a maioria dos programadores se faz nesse momento
como fazer o Javascript abrir e fechar os ns da rvore. a pergunta
errada. Este o momento de se perguntar quais so os status da
aplicao e como a mudana de aparncia que eles representam pode
ser reproduzida no CSS. No caso do nosso exemplo, cada n da
rvore pode ter dois status, aberto e fechado. O status aberto
exatamente como o menu j est aparecendo, ento no precisamos
fazer mais nada para ele. No status fechado, os filhos daquele n
devem ser ocultos. Ento vamos usar o seguinte CSS:
.fechado ul{display:none}

Uma nica linha. Agora, no Javascript, no precisamos nos


preocupar com a aparncia. Nosso script ser um simples controle
de status:
$(function(){
// Capturamos todos os ns que tem filhos
$('ul.tree li:has(ul)>a').click(function(){
// Ao clicar no link, trocamos o status do seu pai (LI)
$(this).parent().toggleClass('fechado')
return false
}).parent().addClass('fechado') // Fechando tudo, para comear
})

E o resultado:

Voc entendeu a ideia. Ao invs de tornar seu javascript verstil e


flexvel, faa-o vinculado a um bom padro de mark-up e folhas de
estilo, e reproduza esse mark-up onde precisar.

4. Ao escrever um plugin jQuery, faa seus mtodos


retornar this. isso o que nos permite encadear as chamadas de
mtodos em jQuery. Imagine, por exemplo, o seguinte plugin:
jQuery.fn.pisca=function(){
this.fadeOut().fadeIn()
}

Se voc inserir um return, assim:


jQuery.fn.pisca=function(){
this.fadeOut().fadeIn()
return this
}

Poder usar sua funo numa sequncia de chamadas jQuery:


$('h2').pisca().append('!')

4. Faa seu site no depender de javascript. Claro, se possvel.


Mas chegue a linha do "possvel" bem mais pra cima, por favor.
APIs do HTML5 que voc pode usar
hoje

HTML5, porque tanto barulho? O desenvolvimento do HTML5


tem alcanado grande repercusso nos meios especializados. Nunca
uma verso do HTML foi to aguardada ou comentada. As verses
anteriores, pelo contrrio, pareciam passar quase despercebidas.
Qual a diferena hoje? Porque tanto barulho em cima de uma
verso do HTML?

Em primeiro lugar, preciso entender o que o HTML5. O HTML


a linguagem base para a publicao de contedo na web, e sempre
esteve focado em contedo e nas melhores maneiras de represent-
lo. H tags (comandos) para criar ttulos, pargrafos, citaes, listas,
links, imagens, tabelas, glossrios, notas e at revises no texto. At
recentemente, cada nova verso do HTML apenas ampliava e
melhorava as maneiras de marcar esse contedo. Alm disso,
haviam tags para criar formulrios simples e no muito mais do que
isso.

Mas a web evoluiu. J h dez anos que foi inventado o Ajax. Alm de
publicar contedo, a web a maior plataforma de computao do
planeta. Comeando pelos nossos e-mails, o Ajax permitiu uma
gradual, porm muito rpida, migrao dos nossos softwares das
diversas plataformas para a web. Mercado aps mercado, os
softwares de gesto e controle foram migrando para a web. Mesmo
as aplicaes de uso comum em Desktops, como os pacotes de
escritrio e editores de imagens, possuem hoje alternativas web que,
embora ainda falte um pouco para serem equivalentes, atendem s
necessidades da maioria dos mortais.

A tecnologia envolvida tambm evoluiu. As linguagens de


programao, os frameworks, e mesmo as maneiras de se manter e
hospedar um site deram saltos significativos nos ltimos dez anos. O
conceito de Cloud Computing no existia h dez anos, s para citar
um exemplo. Mas o HTML no estava evoluindo. Como construir
uma aplicao complexa como um editor de imagens com uma
linguagem que s tem comandos para pargrafos, ttulos e
formulrios simples? Tudo o que foi feito at hoje em termos de
Ajax e aplicaes web ricas ou usa plugins proprietrios, como o
Flash e o Silverlight, ou foi construdos com adaptaes do HTML,
coisa muito prxima do que poderia ser descrito pela palavra
"gambiarra".

Para que voc tenha uma ideia do que estamos falando, saiba que as
listas de e-mails do Gmail so sries de tags DIV aninhadas, com
algumas SPAN para as labels e outros detalhes de interface. DIV e
SPAN no so tags que descrevem, de maneira nenhuma, uma tela
de listagem de e-mails. Mas simplesmente no h, hoje, uma
maneira melhor de construir essa tela.

por isso que HTML5 to importante. Diferente das outras


verses da linguagem, estamos falando de uma evoluo substancial
no que o prprio HTML5. Alm de incluir novos elementos,
ampliando as possibilidades semnticas do HTML, a nova verso da
linguagem possui todo um novo conjunto de elementos de
formulrio, uma sublinguagem para o desenho de grficos vetoriais
(SVG), e um vasto conjunto de novas APIs que permitem, usando
javascript, armazenar dados na mquina do usurio, desenhar
bitmaps, obter dados de geolocalizao, controlar o caching e o
funcionamento da aplicao quando o usurio estiver offline, alm
de uma poro de outros pequenos detalhes.

Pela primeira vez temos uma verso do HTML que no foi feita
apenas para a publicao de texto, mas para a construo de
aplicaes web. Alm disso, pela primeira temos recursos no HTML
para aplicaes offline e para o desenvolvimento de aplicaes web
para dispositivos mveis.

Viso Geral
Entre as novidades de HTML5, podemos destacar os seguintes
grupos de recursos:

1. Novos elementos semnticos. Ou seja, novas tags. Os novos


elementos foram criados atravs de um extenso estudo estatstico
do que os desenvolvedores estavam de fato usando. Assim, temos
tags para elementos de navegao, contedo relacionado, sees e
artigos, grupos de ttulos, barras de ferramentas e menus de
comando, para citar alguns exemplos.

2. Elementos de udio e vdeo. At ento, no havia uma maneira


padronizada de se publicar vdeo na web. Voc sempre precisava
contar com a presena de um plugin na mquina do usurio para
exibir o vdeo. Temos sites hoje com vdeo publicado usando como
player os plugins do Flash, QuickTime, Windows Media, Real Media
e Silverlight, s para citar alguns. Com HTML5, nada disso
necessrio. Basta inserir um elemento video no HTML e o
navegador saber como toc-lo. E como o vdeo um elemento
comum do HTML, muito fcil acessar suas propriedades e
interagir com ele usando Javascript.

3. Novos recursos de formulrio. Isso inclui novos elementos, como


um slider, um calendrio (nativo do navegador, sem plugins de
jQuery) e um color picker. Inclui tambm uma nova API para
validao de dados. Qualquer um que j construiu validao de
formulrios em Javascript ficar grato aos criadores do HTML5 por
esse recurso, que permite coisas como validar um campo de
formulrio simplesmente inserindo um atributo com uma expresso
regular.

4. Novos recursos para comunicao com o servidor. Incluindo


conexes HTTP persistentes, um modelo de eventos disparados do
servidor para o cliente, um abrangente controle de caching e APIs
para aplicaes offline.

5. Novos recursos para representao e armazenamento de dados,


incluindo uma nova estrutura semntica, os microdados, que
permite descrever com atributos do HTML praticamente qualquer
estrutura de dados que possa ser descrita em RDF, alm de APIs
para o armazenamento de estruturas de dados na mquina do
usurio.
6. A API canvas, que permite o desenho e manipulao de grficos
bitmap atravs de Javascript. As possibilidades so inmeras,
comeando com a criao de editores de imagem acessveis pela
web. O uso dessa API para jogos tambm parece bastante promissor.

7. As sublinguagens SVG e MathML, que permitem,


respectivamente, publicar grficos vetoriais e frmulas matemticas.
A publicao de grficos vetoriais, acessveis atravs de Javascript,
torna desnecessrios plugins como Flash e Silverlight para a
esmagadora maioria de seus casos de uso.

8. A API de geolocalizao, que permite obter, via Javascript, a


posio do usurio no mundo, obtendo sua latitude e longitude. Com
isso, fcil desenh-lo num mapa, mostrar o que h por perto ou
exibir contedo personalizado de acordo com sua localizao.

Usando a imaginao
O HTML5 ainda est sendo desenvolvido e, embora os navegadores
o estejam adotando muito rapidamente, seu uso geral ainda tem
restries que o tornam uma sombra do que esperamos que ele
venha a ser. Apesar disso, muitos de seus recursos j podem ser
usados para plataformas especficas, e parte deles j est pronta
para o uso geral. O cenrio que descreveremos a seguir j
perfeitamente possvel hoje:

"Marcelo trabalha numa empresa de entregas. Ele acaba de receber


um novo smartphone, de um modelo cujo navegador compatvel
com os recursos do HTML5 usados no sistema de sua empresa.
Como o sistema baseado em web, ele no precisa se preocupar em
instalar nada em seu telefone, simplesmente acessa o endereo do
sistema e digita seu login e senha. Automaticamente carregada na
tela a lista das entregas que ele precisa fazer hoje. So doze. O
sistema tambm apresenta algumas rotas sugeridas e Marcelo
escolhe uma delas. Enquanto dirige, Marcelo acompanha seu trajeto
no mapa, que atualizado de tempos em tempos com as informaes
do trnsito. Ao mesmo tempo a central de monitoramento da
empresa atualizada constantemente com a localizao de Marcelo,
sua velocidade e a situao de suas entregas.

Mas alguns contratempos se colocam entre Marcelo e suas entregas.


O primeiro deles uma rea de sombra GPRS. Voc sabe, celulares
no funcionam to bem assim em todos os lugares. E numa rea
considervel por onde Marcelo ter de passar, apesar das constantes
reclamaes dos usurios operadora de telefonia mvel, ainda no
h sinal de celular. Felizmente, o navegador de seu telefone
armazenou os dados das entregas e rotas para uso offline. Ao entrar
na rea de sombra, Marcelo some dos monitores da central. Mas seu
telefone continua armazenando sua posio de minuto em minuto, e
continua mostrando o mapa e a rota que Marcelo tem que seguir.

Assim que ele sai da rea de sombra, a central atualizada com o


caminho que ele percorreu e sua velocidade em cada trecho do
trajeto. Essa informao muito importante, pois o clculo das
melhores rotas baseado nela.

Aps a quinta entrega da manh, Marcelo resolve que hora de


almoar. A empresa disponibiliza uma srie de locais nas quatro
zonas da cidade onde seus entregadores podem estacionar e comer
num restaurante conveniado. Com alguns cliques na tela, Marcelo
pode ver os restaurantes prximos de onde est e escolher um. A
rota recalculada para incluir a passagem por ali.

Durante o almoo Marcelo aproveita para consultar seus relatrios


de performance no sistema. So apresentados seus grficos de
quantidade entregas e quilometragem, as duas mtricas mais
importantes em seu trabalho. Um ponto muito baixo na linha do
grfico chama sua ateno. Como o grfico interativo, clicando
nesse ponto o trecho do grfico ampliado e ele pode ver mais
detalhes sobre esse dia. Mais um clique mostra seu relatrio do dia.
Ah, claro, o dia em que ele adoeceu. De fato, trabalhou s no
perodo da manh.

Um novo obstculo se interpe entre Marcelo e suas entregas. Logo


antes de sair para o almoo, seu novo smartphone para de funcionar.
Sem nenhum aviso, simplesmente apaga a tela e se recusa a ligar.
Felizmente, Marcelo trouxe em sua bolsa o smartphone antigo. Ele
sempre faz isso quando troca de telefone, fica alguns dias carregando
o antigo na bolsa at ter certeza de que tudo vai funcionar. Dessa vez
sua precauo vai ser til. Novamente, basta acessar uma URL para
que o mapa com as rotas de entrega da tarde seja exibido. O telefone
comea ento a fazer o caching dos dados de rota, para o caso de
Marcelo precisar passar por outra rea de sombra GPRS. Mas
Marcelo no precisa esperar, pode comear as entregas da tarde pois
a aplicao funciona normalmente enquanto o caching feito.

Um ltimo problema ainda o aguarda. A penltima entrega numa


rea de ruas estreitas, onde bem difcil se localizar. Marcelo vai ter
que usar bastante o zoom do mapa. Felizmente, como o mapa
construdo com vetores em SVG, o zoom instantneo."

Voc j pode desenvolver em HTML5


Embora voc no possa usar todos os recursos (alguns deles ainda
nem foram descritos) voc j pode comear a trabalhar com HTML5.
E h algumas boas razes para faz-lo. A primeira delas que a nova
semntica do HTML5 realmente melhor, tanto para quem escreve
HTML quanto para os motores de busca que vo index-lo. Alm
disso, a existncia de tags especficas para determinadas funes vai
reduzir a quantidade de divs aninhados e atributos class necessrios,
deixando o cdigo ligeiramente mais enxuto e bem mais fcil de
manter.

Voc pode oferecer alternativas em Javascript ou atravs de plugins


para parte dos recursos que no so suportados por todos os
navegadores. Por exemplo, voc pode publicar vdeo com a tag
video, e oferecer um player em Flash para os usurios de Internet
Explorer. Pode usar a Local Storage API para os navegadores que a
suportam, e usar cookies naqueles que no tem suporte a ela ainda.
Obviamente que o player em Flash e o storage em cookies tem
importantes limitaes, sendo prefervel a verso HTML5. Ento,
por que no oferecer uma verso melhor a quem pode ter acesso a
ela?

Adicionalmente, voc pode oferecer novos recursos e facilidades em


seu site somente para os navegadores que tiverem suporte a eles. O
Google Maps faz isso, por exemplo, exibindo um boto "mostrar
meu local" apenas nos navegadores que tem suporte a
geolocalizao. No vai doer nada nos usurios de navegadores sem
o recurso que, afinal de contas, no tero acesso a isso de um jeito
ou de outro. Mas os usurios de navegadores mais capazes vo
agradec-lo.

Por fim, muitos outros recursos do HTML5 faro sentido se voc


estiver falando de desenvolver para uma nica plataforma ou para
um conjunto de plataformas especfico. Por exemplo, se sua fora de
vendas usa Android ou iPhone OS, voc est falando de um conjunto
de navegadores com suporte a HTML5 bem mais maduro que a
mdia dos navegadores para Desktop, e cujo suporte plenamente
conhecido por voc. Isso pode tornar as coisas muito mais
divertidas. A verso para iPad de seu site ou a verso para
smartphones de uma aplicao podem ser aplicaes Ajax realmente
ricas com HTML5.

Por onde comear?


Naturalmente, voc pode ler as especificaes do HTML5
diretamente no site do W3C

A documentao estritamente formal, feita para que


desenvolvedores de navegador, por exemplo, possam construir a
nova gerao de navegadores com suporte a HTML5. Como ela no
foi escrita para ensinar os desenvolvedores web a usar a nova verso
da linguagem, voc pode ach-la bastante rida. Pensando nisso, o
W3C Brasil contratou a Visie para desenvolver a apostila de seu
curso oficial de HTML5, que pode ser encontrada, sob uma licena
Creative Commons, no Tableless. Bom proveito!
Acesse o HTML5 Please. Clique em "use" e d uma olhada na lista.
Agora clique em "use with caution" e confira a lista. Viu quanta
coisa? Por que a maioria dos exemplos de site em HTML5
brasileiros, dois anos depois de comearmos a usar esse treco, ainda
so um mark-up levemente vitaminado e canvas?

Onde esto nossas aplicaes offline? Web sockets? Drag-and-drop?


Geolocation? Micro-data? Device orientation? Novos campos de
formulrio? SVG? History API?

Mas no tem demanda


Voc pode se desculpar por estar usando os mesmos velhos recursos
de sempre, dizendo que os clientes da agncia ou produtora onde
voc trabalha no querem os recursos novos, que seu chefe no quer
saber dessas coisas, que tem trabalho pra caramba pra
simplesmente recortar os layouts que recebe e no quer arrumar
sarna pra se coar

Voc vai mesmo querer passar o resto da vida recortando layouts? O


mundo vai mudar, e voc vai ser extinto, dinossauro. Se no tem
demanda, crie a demanda. Comece a desenvolver projetos pessoais
com o que voc acha que seus clientes deveriam estar usando. Em
seguida, mostre para todo mundo. Voc vai ver se a demanda no
aparece.

Todo mundo tem celular conectado. Todos os navegadores (at o IE)


esto se esforando para funcionar direito. um momento mgico.
uma oportunidade que voc no quer deixar passar. Um pouquinho
de esforo a, por favor.