JavaScript Eloquente
JavaScript Eloquente
Introduction
1.1
1.2
Estrutura do Programa
1.3
Funes
1.4
1.5
1.6
1.7
1.8
1.9
Expresses Regulares
1.10
Mdulos
1.11
1.12
JavaScript e o Navegador
1.13
1.14
Manipulando Eventos
1.15
1.16
Desenhando no Canvas
1.17
HTTP
1.18
1.19
1.20
[Link]
1.21
1.22
Contedo do Livro
Introduo
1. Valores, Tipos e Operadores - (Parte 1: Linguagem)
2. Estrutura do Programa
3. Funes
4. Estrutura de Dados: Objeto e Array
5. Funes de Ordem Superior
6. A Vida Secreta dos Objetos
7. Prtica: Vida Eletrnica
8. Erros e Manipulao de Erros
9. Expresses Regulares
10. Mdulos
2
Como Contribuir?
Se voc tiver interesse em ajudar, criamos um guia para ajud-lo e, se tiver qualquer dvida, basta abrir uma
issue.
Informaes Importantes
Autor: Marijn Haverbeke
Verso original deste livro.
Licenciado sob a licena Creative Commons attribution-noncommercial.
Todo cdigo neste livro tambm pode ser considerado sob a licena MIT.
128
64
32
16
Valores
Imagine um mar de bits, um oceano deles. Um computador moderno possui mais de trinta bilhes de bits em
seu armazenamento voltil de dados. J em armazenamento de dados no volteis, sendo eles o disco rgido ou
algo equivalente, tende a ter uma ordem de magnitude ainda maior.
Para que seja possvel trabalhar com tais quantidades de bits sem ficar perdido, voc pode separ-los em partes
que representam pedaos de informao. No ambiente JavaScript, essas partes so chamadas de valores.
Embora todos os valores sejam compostos por bits, cada valor exerce um papel diferente e todo valor possui um
tipo que determina o seu papel. Existem seis tipos bsicos de valores no JavaScript: nmeros, strings,
Booleanos, objetos, funes e valores indefinidos.
Para criar um valor, voc deve simplesmente invocar o seu nome. Isso bastante conveniente, pois voc no
precisa de nenhum material extra para constru-los e muito menos ter que pagar algo por eles. Voc s chama por
ele e pronto, voc o tem. claro que eles no so criados do nada. Todo valor precisa estar armazenado em
algum lugar e, se voc quiser usar uma quantidade enorme deles ao mesmo tempo, voc pode acabar ficando
sem bits. Felizmente, esse um problema apenas se voc precisa deles simultaneamente. A medida que voc
no utiliza um valor, ele ser dissipado, fazendo com que seus bits sejam reciclados, disponibilizando-os para
serem usados na construo de outros novos valores.
Esse captulo introduz os elementos que representam os tomos dos programas JavaScript, que so os simples
tipos de valores e os operadores que podem atuar sobre eles.
Nmeros
Valores do tipo nmero so, sem muitas surpresas, valores numricos. Em um programa JavaScript, eles so
escritos assim:
13
Coloque isso em um programa e isso far com que padres de bits referentes ao nmero 13 sejam criados e
passe a existir dentro da memria do computador.
O JavaScript utiliza um nmero fixo de bits, mais precisamente 64 deles, para armazenar um nico valor numrico.
Existem apenas algumas maneiras possveis que voc pode combinar esses 64 bits, ou seja, a quantidade de
nmeros diferentes que podem ser representados limitada. Para um valor N de dgitos decimais, a quantidade
de nmeros que pode ser representada 10. De forma similar, dado 64 dgitos binrios, voc pode representar
2 nmero diferentes, que aproximadamente 18 quintilhes (o nmero 18 com 18 zeros aps ele). Isso muito.
A memria do computador costumava ser bem menor e, por isso, as pessoas usavam grupos de 8 ou 16 bits
para representar os nmeros. Por isso, era muito fcil extrapolar essa capacidade de armazenamento to
pequena usando nmeros que no cabiam nesse espao. Hoje em dia, at os computadores pessoais possuem
memria suficiente, possibilitando usar grupos de 64 bits, sendo apenas necessrio se preocupar em exceder o
espao quando estiver lidando com nmeros extremamente grandes.
Entretanto, nem todos os nmeros inteiros menores do que 18 quintilhes cabem em um nmero no JavaScript.
Os bits tambm armazenam nmeros negativos, sendo que um desses bits indica o sinal do nmero. Um grande
problema que nmeros fracionrios tambm precisam ser representados. Para fazer isso, alguns bits so
usados para armazenar a posio do ponto decimal. Na realidade, o maior nmero inteiro que pode ser
armazenado est na regio de 9 quatrilhes (15 zeros), que ainda assim extremamente grande.
Nmeros fracionrios so escritos usando um ponto.
9.81
Para nmeros muito grandes ou pequenos, voc pode usar a notao cientfica adicionando um e (de
expoente) seguido do valor do expoente:
2.998e8
preciso quando existem apenas 64 bits disponveis para armazen-los. Isso vergonhoso, porm causa
problemas apenas em algumas situaes especficas. O mais importante estar ciente dessa limitao e tratar
nmeros fracionrios como aproximaes e no como valores precisos.
Aritmtica
A principal coisa para se fazer com nmeros so clculos aritmticos. As operaes como adio ou
multiplicao recebem dois valores numricos e produzem um novo nmero a partir deles. Elas so
representadas dessa forma no JavaScript:
100 + 4 * 11
Os smbolos
Colocar um operador entre dois valores ir aplic-lo a esses valores e produzir um novo valor.
O significado do exemplo anterior adicione 4 e 100 e, em seguida, multiplique esse resultado por 11 ou a
multiplicao realizada antes da adio? Como voc deve ter pensado, a multiplicao acontece primeiro.
Entretanto, como na matemtica, voc pode mudar esse comportamento envolvendo a adio com parnteses.
(100 + 4) * 11
Quando os operadores aparecem juntos sem parnteses, a ordem que eles sero aplicados determinada pela
precedncia deles. O exemplo mostra que a multiplicao ocorre antes da adio. O operador
mesma precedncia que
possui a
Quando vrios operadores de mesma precedncia aparecem prximos uns aos outros, como por exemplo
+ 1
(1 - 2) + 1
1 - 2
Essas regras de precedncia no so algo que voc deve se preocupar. Quando estiver em dvida, apenas
adicione os parnteses.
Existe mais um operador aritmtico que voc talvez no reconhea imediatamente. O smbolo
representar a operao de resto.
144 % 12
produz
X % Y
o resto da diviso de
por
. Por exemplo,
314 % 100
usado para
produz
14
frequncia esse operador ser chamado de modulo mas, tecnicamente falando, resto o termo mais preciso.
Nmeros Especiais
Existem trs valores especiais no JavaScript que so considerados nmeros, mas no se comportam como
nmeros normais.
Os dois primeiros so
clculo
Infinity - 1
Infinity
-Infinity
continua sendo
Infinity
confie muito em clculos baseados no valor infinito, pois esse valor no matematicamente slido, e
rapidamente nos levar ao prximo nmero especial:
NaN
NaN
a abreviao de not a numb er (no um nmero), mesmo sabendo que ele um valor do tipo nmero.
Voc receber esse valor como resultado quando, por exemplo, tentar calcular
Infinity - Infinity
0 / 0
ou, ento, realizar quaisquer outras operaes numricas que no resultem em um nmero
preciso e significativo.
Strings
O prximo tipo bsico de dado a string. Strings so usadas para representar texto, e so escritas delimitando o
seu contedo entre aspas.
"Patch my boat with chewing gum"
'Monkeys wave goodbye'
Ambas as aspas simples e duplas podem ser usadas para representar strings, contanto que as aspas abertas
sejam iguais no incio e no fim.
Quase tudo pode ser colocado entre aspas, e o JavaScript criar um valor do tipo string com o que quer que seja.
Entretanto, alguns caracteres so mais difceis. Voc pode imaginar como deve ser complicado colocar aspas
dentro de aspas. Alm disso, os caracteres newlines (quebra de linhas, usados quando voc aperta Enter),
tambm no podem ser colocados entre aspas. As strings devem permanecer em uma nica linha.
Para que seja possvel incluir tais caracteres em uma string, a seguinte notao utilizada: toda vez que um
caractere de barra invertida (
) for encontrado dentro de um texto entre aspas, ele indicar que o caractere
seguinte possui um significado especial. Isso chamado de escapar o caractere. Uma aspa que se encontra
logo aps uma barra invertida no representar o fim da string e, ao invs disso, ser considerada como parte do
texto dela. Quando um caractere
linha e, de forma similar, um
aparecer aps uma barra invertida, ele ser interpretado como uma quebra de
Existe, obviamente, situaes nas quais voc vai querer que a barra invertida em uma string seja apenas uma
barra invertida e no um cdigo especial. Nesse caso, se duas barras invertidas estiverem seguidas uma da
outra, elas se anulam e apenas uma ser deixada no valor da string resultante. Essa a forma na qual a string
newline character is written like \n.
Ele no efetua a adio, mas concatena, ou seja, junta duas strings em uma nica string. O prximo exemplo
produzir a string
"concatenate"
Existem outras maneiras de manipular as strings, as quais sero discutidas quando chegarmos aos mtodos no
Captulo 4.
Operadores Unrios
Nem todos os operadores so smbolos, sendo que alguns so escritos como palavras. Um exemplo o
operador
typeof
, que produz um valor do tipo string contendo o nome do tipo do valor que voc est verificando.
[Link](typeof 4.5)
// number
[Link](typeof "x")
// string
Ns vamos usar
[Link]
nos cdigos de exemplo para indicar que desejamos ver o resultado da avaliao de
algo. Quando voc executar tais cdigos, o valor produzido ser mostrado na tela, entretanto, a forma como ele
ser apresentado vai depender do ambiente JavaScript que voc usar para rodar os cdigos.
Todos os operadores que vimos operavam em dois valores, mas
typeof
usam dois valores so chamados de operadores b inrios, enquanto que aqueles que recebem apenas um, so
chamados de operadores unrios. O operador
Valores Booleanos
Voc frequentemente precisar de um valor para distinguir entre duas possibilidades, como por exemplo sim e
no, ou ligado e desligado. Para isso, o JavaScript possui o tipo Booleano, que tem apenas dois valores:
verdadeiro e falso (que so escritos como
true
false
respectivamente).
Comparaes
Essa uma maneira de produzir valores Booleanos:
[Link](3 > 2)
// true
[Link](3 < 2)
// false
Os sinais
>
<
respectivamente. Eles so operadores binrios, e o resultado da aplicao deles um valor Booleano que indica
se eles so verdadeiros nesse caso.
Strings podem ser comparadas da mesma forma.
[Link]("Aardvark" < "Zoroaster")
// true
A forma na qual as strings so ordenadas mais ou menos alfabtica. Letras maisculas sero sempre
menores que as minsculas, portanto,
Z < a
assim por diante) tambm so includos nessa ordenao. A comparao de fato, baseada no padro Unicode,
que atribui um nmero para todos os caracteres que voc possa precisar, incluindo caracteres do Grego, rabe,
Japons, Tmil e por a vai. Possuir tais nmeros til para armazenar as strings dentro do computador, pois faz
com que seja possvel represent-las como uma sequncia de nmeros. Quando comparamos strings, o
JavaScript inicia da esquerda para a direita, comparando os cdigos numricos dos caracteres um por um.
Outros operadores parecidos so
>=
igual a).
[Link]("Itchy" != "Scratchy")
// true
<=
==
(igual a) e
!=
(no
Existe apenas um valor no JavaScript que no igual a ele mesmo, que o valor
NaN
NaN
supostamente usado para indicar o resultado de alguma operao que no tenha sentido e, por isso, ele
Operadores Lgicos
Existem tambm operadores que podem ser aplicados aos valores Booleanos. O JavaScript d suporte a trs
operadores lgicos: and, or e not, que podem ser traduzidos para o portugus como e, ou e no. Eles podem ser
usados para "pensar" de forma lgica sobre Booleanos.
O operador
&&
representa o valor lgico and ou, em portugus, e. Ele um operador binrio, e seu resultado
O operador
||
indica o valor lgico or ou, em portugus, ou. Ele produz um valor verdadeiro se qualquer um dos
!true
produz
false
produz
!false
true
Quando misturamos esses operadores Booleanos com operadores aritmticos e outros tipos de operadores,
nem sempre bvio quando devemos usar ou no os parnteses. Na prtica, voc normalmente no ter
problemas sabendo que, dos operadores que vimos at agora,
||
operador
>
&&
operadores. Essa ordem foi escolhida de tal forma que, em expresses tpicas como o exemplo a seguir, poucos
parnteses so realmente necessrios:
1 + 1 == 2 && 10 * 10 > 50
O ltimo operador lgico que iremos discutir no unrio nem binrio, mas ternrio, operando em trs valores.
Ele escrito usando um ponto de interrogao e dois pontos, como mostrado abaixo:
[Link](true ? 1 : 2);
// 1
[Link](false ? 1 : 2);
// 2
Esse operador chamado de operador condicional (algumas vezes chamado apenas de operador ternrio, j
que o nico operador desse tipo na linguagem). O valor presente esquerda do ponto de interrogao
seleciona qual dos outros dois valores ser retornado. Quando ele for verdadeiro, o valor do meio escolhido e,
quando for falso, o valor direita retornado.
Valores Indefinidos
Existem dois valores especiais,
null
undefined
significado. Eles so valores por si ss, mas no carregam nenhum tipo de informao.
Muitas operaes na linguagem que no produzem um valor com significado (voc ver alguns mais para frente)
retornaro
undefined
undefined
null
muita diferena na maioria das vezes. Nos casos em que voc deve realmente se preocupar com esses valores,
recomendo trat-los como valores idnticos (vamos falar mais sobre isso em breve).
Quando um operador aplicado a um tipo de valor errado, o JavaScript converter, de forma silenciosa, esse
valor para o tipo que ele desejar, usando uma srie de regras que muitas vezes no o que voc deseja ou
espera. Esse comportamento chamado de coero de tipo (ou converso de tipo). Portanto, na primeira
expresso,
operador
nmero
+
1
null
se torna
e, na segunda, a string
"5"
se torna o nmero
. J na terceira expresso, o
tenta efetuar uma concatenao de string antes de tentar executar a adio numrica e, por isso, o
convertido para a string
"1"
Quando algo que no pode ser mapeado como um nmero de forma bvia (tais como
convertido para um nmero, o valor
NaN
continuam produzindo
NaN
NaN
"five"
ou
undefined
, portanto, quando voc perceber que est recebendo esse valor em algum lugar
==
NaN
os tipos forem diferentes, o JavaScript usa um conjunto de regras complicadas e confusas para determinar o que
fazer, sendo que, na maioria dos casos, ele tenta apenas converter um dos valores para o mesmo tipo do outro
valor. Entretanto, quando
null
ou
undefined
null
ou
10
[Link](null == undefined);
// true
[Link](null == 0);
// false
O ltimo exemplo um comportamento que normalmente bastante til. Quando quiser testar se um valor possui
um valor real ao invs de
(ou
!=
ou
null
undefined
null
com o operador
==
).
true
false
NaN
0 == false
false
, enquanto todos os
"" == false
retornam
true
Para casos assim, onde voc no quer qualquer converso automtica de tipos acontecendo, existem dois tipos
extras de operadores:
===
!==
Usar os operadores de comparao de trs caracteres defensivamente, para prevenir inesperadas converses de
tipo que o faro tropear, algo que eu recomendo. Mas quando voc tem certeza de que os tipos de ambos os
lados sero iguais, ou que eles vo ser ambos
null
undefined
curtos.
O Curto-Circuito de && e ||
Os operadores lgicos
&&
||
tem uma maneira peculiar de lidar com valores de tipos diferentes. Eles vo
converter o valor sua esquerda para o tipo booleano a fim de decidir o que fazer, mas ento, dependendo do
operador e do resultado da converso, eles ou retornam o valor esquerda original, ou o valor direita.
O operador
||
vai retornar o valor sua esquerda quando ele puder ser convertido em
true
, ou valor sua
direita caso contrrio. Ele faz a coisa certa para valores booleanos, e vai fazer algo anlogo para valores de outros
tipos. Isso muito til, pois permite que o operador seja usado para voltar um determinado valor predefinido.
[Link](null || "user")
// user
[Link]("Karl" || "user")
// Karl
O operador
false
&&
trabalha similarmente, mas ao contrrio. Quando o valor sua esquerda algo que se torne
, ele retorna o valor, e caso contrrio ele retorna o valor sua direita.
Outro importante propriedade destes 2 operadores que a expresso a sua direita avaliada somente quando
necessrio. No caso de
true || X
, no importa o que
false && X
Resumo
Ns vimos 4 tipos de valores do JavaScript neste captulo. Nmeros, strings, booleanos e valores indefinidos.
Alguns valores so criados digitando seu nome (
true
null
) ou valores (13,
"abc"
), comparao (
==
11
!=
===
!==
<
>
<=
>=
&&
||
) e lgica (
,e
),
typeof
12
Estrutura do Programa
O meu corao vermelho brilha nitidamente sob minha pele e ele tm que administrar 10cc de JavaScript
para fazer com que eu volte (Eu respondi bem a toxinas no sangue). Cara, esse negcio vai chutar os
pssegos de direita para fora!
_why, Why's (Poignant) Guide to Ruby
Este o ponto onde ns comeamos a fazer coisas que podem realmente ser chamadas de programao. Ns
vamos expandir nosso domnio da linguagem JavaScript para alm dos substantivos e fragmentos de sentenas
que ns vimos anteriormente, para o ponto onde poderemos realmente expressar algo mais significativo.
Expresses e Afirmaes
No Captulo 1 ns criamos alguns valores e ento aplicamos operadores para obter novos valores. Criar valores
desta forma uma parte essencial de todo programa JavaScript, mas isso somente uma parte. Um fragmento
de cdigo que produz um valor chamado de expresso. Todo valor que escrito literalmente (como
"psychoanalysis"
22
ou
) uma expresso. Uma expresso entre parnteses tambm uma expresso, e tambm um
um programa intil, entretanto. Uma expresso pode ser apenas para produzir um valor, que pode ento ser
usado para fechar a expresso. Uma declarao vale por si s, e s equivale a alguma coisa se ela afeta em
algo. Ela pode mostrar algo na tela - que conta como mudar algo - ou pode mudar internamente o estado da
mquina de uma forma que vai afetar outras declaraes que iro vir. Estas mudanas so chamadas efeitos
colaterais. As afirmaes nos exemplos anteriores somente produzem o valor
true
e ento imediatamente
os jogam fora novamente. No deixam nenhuma impresso no mundo. Quando executamos o programa, nada
acontece.
Ponto e vrgula
Em alguns casos, o JavaScript permite que voc omita o ponto e vrgula no fim de uma declarao. Em outros
casos ele deve estar l ou coisas estranhas iro acontecer. As regras para quando ele pode ser seguramente
omitido so um pouco complexas e propensas a erro. Neste livro todas as declaraes que precisam de ponto e
vrgula vo sempre terminar com um. Eu recomendo a voc fazer o mesmo em seus programas, ao menos at
voc aprender mais sobre as sutilezas envolvidas em retirar o ponto e vrgula.
Variveis
13
Como um programa mantm um estado interno? Como ele se lembra das coisas? Ns vimos como produzir
novos valores com valores antigos, mas isso no altera os valores antigos, e o valor novo deve ser imediatamente
usado ou vai ser dissipado. Para pegar e guardar valores, o JavaScript fornece uma coisa chamada varivel.
var caught = 5 * 5;
var
vai definir uma varivel. Ela seguida pelo nome da varivel e, se ns quisermos d-la imediatamente um valor,
por um operador
e uma expresso.
caught
multiplicao 5 por 5.
Depois de uma varivel ter sido definida, seu nome pode ser usado como uma expresso. O valor da expresso
o valor atual mantido pela varivel. Aqui temos um exemplo:
var ten = 10;
[Link](ten * ten);
// 100
Nomes de variveis podem ser quase qualquer palavra, menos as reservadas para palavras-chave (como
No pode haver espaos includos. Dgitos podem tambm ser parte dos nomes de variveis -
catch22
var
).
um
nome vlido, por exemplo - mas um nome no pode iniciar com um dgito. O nome de uma varivel no pode
incluir pontuao, exceto pelos caracteres
Quando uma varivel aponta para um valor, isso no significa que estar ligada ao valor para sempre. O operador
=
pode ser usado a qualquer hora em variveis existentes para desconect-las de seu valor atual e ento
Voc deve imaginar variveis como tentculos, ao invs de caixas. Elas no contm valores; elas os agarram duas variveis podem referenciar o mesmo valor. Somente os valores que o programa mantm tem o poder de
ser acessado por ele. Quando voc precisa se lembrar de algo, voc aumenta o tentculo para segurar ou
recoloca um de seus tentculos existentes para fazer isso.
Quando voc define uma varivel sem fornecer um valor a ela, o tentculo fica conceitualmente no ar - ele no tem
nada para segurar. Quando voc pergunta por um valor em um lugar vazio, voc recebe o valor
14
undefined
Um exemplo. Para lembrar da quantidade de dlares que Luigi ainda lhe deve, voc cria uma varivel. E ento
quando ele lhe paga 35 dlares, voc d a essa varivel um novo valor.
var
so chamadas keywords (palavras-chave). Existe tambm algumas palavras que so reservadas para uso em
futuras verses do JavaScript. Estas tambm no so oficialmente autorizadas a serem utilizadas como nomes
de variveis, embora alguns ambientes JavaScript as permitam. A lista completa de palavras-chave e palavras
reservadas um pouco longa:
break
in
try
case
catch
instanceof
typeof
continue
interface
var
void
let
while
debugger
new
with
default
null
yield
delete
package
do
private
else
false
protected
finally
public
for
return
function
static
if
switch
implements
throw
true
this
No se preocupe em memoriz-las, mas lembre-se que este pode ser o problema quando algo no funcionar
como o esperado.
O Ambiente
A coleo de variveis e seus valores que existem em um determinado tempo chamado de
environment
(ambiente). Quando um programa inicia, o ambiente no est vazio. Ele ir conter no mnimo o nmero de
variveis que fazem parte do padro da linguagem. E na maioria das vezes haver um conjunto adicional de
variveis que fornecem maneiras de interagir com o sistema envolvido. Por exemplo, em um navegador, existem
variveis que apontam para funcionalidades que permitem a voc inspecionar e influenciar no atual carregamento
do website, e ler a entrada do mouse e teclado da pessoa que est usando o navegador.
Funes
15
function
programa envolvido por um valor. Este valor pode ser aplicado a fim de executar o programa envolvido. Por
exemplo, no ambiente do navegador, a varivel
alert
Executar uma funo denominado invocar, chamar ou aplicar uma funo. Voc pode chamar uma funo
colocando os parnteses depois da expresso que produz um valor de funo. Normalmente voc ir usar o
nome da varivel que contm uma funo diretamente. Os valores entre os parnteses so passados ao
programa dentro da funo. No exemplo, a funo
usou a
alert
string
alert
arguments
(argumentos). A
precisa somente de um deles, mas outras funes podem precisar de diferentes quantidades ou
tipos de argumentos.
A Funo
A funo
alert
[Link]
pode ser til como sada do dispositivo quando experimentada, mas clicar sempre em todas
estas pequenas janelas vai lhe irritar. Nos exemplos passados, ns usamos
[Link]
maioria dos sistemas JavaScript (incluindo todos os navegadores modernos e o [Link]), fornecem uma funo
[Link]
que escreve seus argumentos como texto na sada do dispositivo. Nos navegadores, a sada fica no
F12
browser
Command + option + I
. Se isso no funcionar,
busque no menu algum item pelo nome de web console ou developer tools.
Quando rodarmos os exemplos ou seu prprio cdigo nas pginas deste livro, o
[Link]
[Link]
claramente contm um
ponto. Eu no tinha mentido para voc. Esta no uma simples varivel, mas na verdade uma expresso que
retorna o campo
log
console
Retornando Valores
Mostrar uma caixa de dilogo ou escrever texto na tela um efeito colateral. Muitas funes so teis por causa
dos efeitos que elas produzem. tambm possvel para uma funo produzir um valor, no caso de no ser
necessrio um efeito colateral. Por exemplo, temos a funo
entre eles:
[Link]([Link](2, 4));
16
[Link]
return
uma expresso, o que significa que chamadas de funo podem ser usadas dentro de expresses maiores. No
exemplo abaixo, uma chamada para a funo
[Link]
, que o oposto de
[Link]
prompt
confirm
O ambiente fornecido pelos navegadores contm algumas outras funes para mostrar janelas. Voc pode
perguntar a um usurio uma questo Ok/Cancel usando
usurio clica em OK e
false
confirm
true
se o
prompt
pode ser usado para criar uma questo "aberta". O primeiro argumento a questo; o segundo o texto
que o usurio inicia. Uma linha do texto pode ser escrita dentro da janela de dilogo, e a funo vai retornar isso
como uma string.
prompt("Tell me everything you know.", "...");
Estas duas funes no so muito usadas na programao moderna para web, principalmente porque voc no
tem controle sobre o modo que a janela vai aparecer, mas elas so teis para experimentos.
Fluxo de Controle
Quando seu programa contm mais que uma declarao, as declaraes so executadas, previsivelmente, de
cima para baixo. Como um exemplo bsico, este programa tem duas declaraes. A primeira pergunta ao usurio
por um nmero, e a segunda, que executada posteriormente, mostra o quadrado deste nmero:
var theNumber = Number(prompt("Pick a number", ""));
alert("Your number is the square root of " + theNumber * theNumber);
A funo
Number
um valor do tipo
string
17
String
prompt
Boolean
Aqui podemos ver uma representao bem trivial do fluxo de controle em linha reta:
Execuo Condicional
Executar declaraes em ordem linear no a nica opo que temos. Uma alternativa a execuo condicional,
onde escolhemos entre duas rotas diferentes baseado em um valor booleano, como ilustra a seguir:
if
que algum cdigo seja executado se, e somente se, uma certa condio existir. No programa anterior, por
exemplo, podemos mostrar o quadrado do dado fornecido como entrada apenas se ele for realmente um nmero.
var theNumber = Number(prompt("Pick a number", ""));
if (!isNaN(theNumber))
alert("Your number is the square root of " +
theNumber * theNumber);
Com essa modificao, se voc fornecer "queijo" como argumento de entrada, nenhuma sada ser retornada.
A palavra-chave
if
expresso escrita entre parnteses logo aps a palavra-chave e seguida por uma declarao a ser executada.
A funo
funo
isNaN
Number
NaN
true
NaN
theNumber
Voc frequentemente no ter cdigo que executa apenas quando uma condio for verdadeira, mas tambm
cdigo que lida com o outro caso. Esse caminho alternativo representado pela segunda seta no diagrama. A
palavra-chave
else
.A
quando voc fornece a ela uma string que no representa um nmero vlido. Por isso,
if
18
if
else
temos um exemplo:
var num = Number(prompt("Pick a number", "0"));
if (num < 10)
alert("Small");
else if (num < 100)
alert("Medium");
else
alert("Large");
num
menor que 10. Se for, ele escolhe esse caminho, mostra "Small"
else
if
. Se a segunda
condio (< 100) for verdadeira, o nmero est entre 10 e 100, e "Medium" ser mostrado. Caso contrrio, o
segundo e ltimo
else
ser escolhido.
Loops While e Do
Considere um programa que imprime todos os nmeros pares de 0 a 12. Uma forma de escrever isso :
[Link](0);
[Link](2);
[Link](4);
[Link](6);
[Link](8);
[Link](10);
[Link](12);
Isso funciona, mas a ideia de escrever um programa fazer com que algo seja menos trabalhoso, e no o
contrrio. Se precisarmos de todos os nmeros pares menores do que 1.000, essa abordagem seria invivel. O
que precisamos de uma maneira de repetir cdigo. Essa forma de fluxo de controle chamada de lao de
repetio (loop).
19
O fluxo de controle do loop nos permite voltar a um mesmo ponto no programa onde estvamos anteriormente e
repeti-lo no estado atual do programa. Se combinarmos isso a uma varivel contadora, conseguimos fazer algo
assim:
var number = 0;
while (number <= 12) {
[Link](number);
number = number + 2;
}
// 0
// 2
//
etcetera
while
if
while
declarao enquanto a expresso produzir um valor que, aps convertido para o tipo Booleano, seja
true
Nesse loop, queremos imprimir o nmero atual e somar dois em nossa varivel. Sempre que precisarmos
executar mltiplas declaraes dentro de um loop, ns as envolvemos com chaves (
). As chaves, para
declaraes, so similares aos parnteses para as expresses, agrupando e fazendo com que sejam tratadas
como uma nica declarao. Uma sequncia de declaraes envolvidas por chaves chamada de b loco.
Muitos programadores JavaScript envolvem cada
if
consistncia quanto para evitar que seja necessrio adicionar ou remover chaves quando houver alteraes
posteriores no nmero de declaraes. Nesse livro, para sermos mais breves, iremos escrever sem chaves a
maioria das declaraes compostas por uma nica linha. Fique a vontade para escolher o estilo que preferir.
A varivel
number
demonstra uma maneira na qual variveis podem verificar o progresso de um programa. Toda
12
number
incrementado por
Como um exemplo de algo que seja til, podemos escrever um programa que calcula e mostra o valor de 2 (2
elevado dcima potncia). Ns usamos duas variveis: uma para armazenar o resultado e outra para contar
quantas vezes multiplicamos esse resultado por 2. O loop testa se a segunda varivel j atingiu o valor 10 e ento
atualiza ambas as variveis.
var result = 1;
var counter = 0;
while (counter < 10) {
result = result * 2;
counter = counter + 1;
}
[Link](result);
// 1024
20
<= 10
do
while
do
sempre executa
suas declaraes ao menos uma vez e inicia o teste para verificar se deve parar ou no apenas aps a primeira
execuo. Para demonstrar isso, o teste aparece aps o corpo do loop:
do {
var name = prompt("Who are you?");
} while (!name);
[Link](name);
Esse programa ir forar voc a informar um nome. Ele continuar pedindo at que seja fornecido um valor que
no seja uma string vazia. Aplicar o operador
neg-lo, e todas as strings exceto
""
faz com que o valor seja convertido para o tipo Booleano antes de
convertem para
true
Indentando Cdigo
Voc deve ter reparado nos espaos que coloco em algumas declaraes. No JavaScript, eles no so
necessrios e o computador ir aceitar o programa sem eles. De fato, at as quebras de linhas so opcionais. Se
voc quiser, pode escrever um programa inteiro em uma nica linha. O papel da indentao dentro dos blocos
fazer com que a estrutura do cdigo se destaque. Em cdigos complexos, onde temos blocos dentro de blocos,
pode se tornar extremamente difcil distinguir onde um bloco comea e o outro termina. Com a indentao
adequada, o formato visual do programa corresponde ao formato dos blocos contidos nele. Gosto de usar dois
espaos para cada bloco, mas essa preferncia pode variar algumas pessoas usam quatro espaos e outras
usam caracteres "tab".
Loops For
Vrios loops seguem o padro visto nos exemplos anteriores do
while
while
teste que normalmente checa se o contador alcanou algum limite. O contador atualizado no final do corpo do
loop, permitindo acompanhar o progresso.
Por esse padro ser muito comum, o JavaScript e linguagens similares fornecem uma forma um pouco mais
curta e compreensiva chamada de loop
for
etcetera
Esse programa equivalente ao exemplo anterior que imprime nmeros pares. A nica diferena que todas as
declaraes relacionadas ao "estado" do loop esto agora agrupadas.
Os parnteses aps a palavra-chave
for
e vrgula inicializa o loop, normalmente definindo uma varivel. A segunda parte a expresso que verifica se o
loop deve continuar ou no. A parte final atualiza o estado do loop aps cada iterao. Na maioria dos casos,
essa construo menor e mais clara que a do
Aqui est o cdigo que calcula 2 usando
for
while
ao invs de
21
while
var result = 1;
for (var counter = 0; counter < 10; counter = counter + 1)
result = result * 2;
[Link](result);
// 1024
break
false
break
) uma maneira fcil de testar se um nmero divisvel por outro. Se for, o resto da
for
nesse exemplo no contm a parte que checa pelo fim do loop. Isso significa que o loop no
true
break
break
, seu programa ficar preso em um loop infinito. Um programa preso em um loop infinito nunca vai
continue
similar ao
break
encontrado no corpo de um loop, o controle de execuo pula para fora do corpo e continua
counter += 1;
22
result *= 2
para dobrar o
result
ou
counter -= 1
Para
counter += 1
counter -= 1
counter++
counter--
switch
H um construtor chamado
switch
Infelizmente, a sintaxe JavaScript usada para isso (que foi herdada na mesma linha de linguagens de
programao, C e Java) um pouco estranha - frequentemente uma cadeia de declaraes
if
continua
switch
switch
fornece, ou para
default
case
ele comea a executar as declaraes, e continua a passar pelos rtulos, at encontrar uma declarao
Em alguns casos, como no exemplo
cases
um
case "sunny"
break
break
cloudy
, o que far com que o programa execute cdigo que voc no gostaria de executar.
Capitalizao
Nomes de variveis no podem conter espaos, no entanto muito til usar mltiplas palavras para descrever
claramente o qu a varivel representa. Estas so praticamente suas escolhas para escrever nomes de variveis
com vrias palavras:
23
fuzzylittleturtle
fuzzy_little_turtle
FuzzyLittleTurtle
fuzzyLittleTurtle
O primeiro estilo difcil de ler. Pessoalmente, eu gosto de usar sublinhados, embora esse estilo seja um pouco
doloroso de escrever. O padro das funes em JavaScript, e o da maioria dos programadores JavaScript,
seguir o ltimo estilo - eles capitalizam toda palavra exceto a primeira. No difcil se acostumar com coisas
pequenas assim, e o cdigo com estilos de nomenclaturas mistas pode se tornar desagradvel para leitura,
ento vamos seguir esta conveno.
Em alguns casos, como a funo
Number
marcar a funo como um construtor. O que um construtor ser esclarecido no captulo 6. Por enquanto, o
importante no ser incomodado por esta aparente falta de consistncia.
Comentrios
Frequentemente, o cdigo puro no transmite todas as informaes necessrias que voc gostaria que tivessem
para leitores humanos, ou ele se transmite de uma forma to enigmtica que as pessoas realmente no
conseguem entend-lo. Em outras ocasies, voc est apenas se sentindo potico ou quer anotar alguns
pensamentos como parte de seu programa. Os comentrios so para isto.
O comentrio um pedao de texto que parte de um programa mas completamente ignorado pelo
computador. No JavaScript temos duas maneiras de escrever os comentrios. Para escrever em uma nica linha
de comentrio, voc pode usar dois caracteres barra (
//
Um
// comentrio
/*
*/
se ele contm quebras de linha. Isto geralmente til para adicionar blocos de informao sobre um arquivo ou
um pedao do programa.
/*
I first found this number scrawled on the back of one of
my notebooks a few years ago. Since then, it has
occasionally dropped by, showing up in phone numbers and
the serial numbers of products that I bought. It
obviously likes me, so I've decided to keep it.
*/
var theNumber = 11213;
Resumo
Voc agora sabe que um programa construdo de declaraes, que as vezes contm mais declaraes.
Declaraes tendem a conter expresses, que podem ser feitas de pequenas expresses.
24
Colocar declaraes uma aps a outra nos d um programa que executado de cima para baixo. Voc pode
causar transtornos no fluxo de controle usando declaraes condicionais (
do
for
else
switch
) e loops (
while
).
As variveis podem ser usadas para arquivar pedaos de dados sob um nome, e so teis para rastrear o estado
de um programa. O ambiente um conjunto de variveis que so definidas. O sistema JavaScript sempre coloca
um nmero padro de variveis teis dentro do seu ambiente.
Funes so valores especiais que encapsulam um pedao do programa. Voc pode invoc-las escrevendo
function Name (argument1, argument2) {}
Exerccios
Se voc est inseguro sobre como testar suas solues para os exerccios, consulte a introduo.
Cada exerccio comea com a descrio de um problema. Leia e tente resolv-lo. Se voc tiver dificuldades,
considere a leitura das dicas abaixo do exerccio. As solues completas para os exerccios no esto inclusas
neste livro, mas voc pode procurar elas onlines em [Link]/code. Se voc quer aprender algo, eu
recomendo que veja as solues somente aps ter resolvido o exerccio, ou pelo menos, depois que tentou por
um perodo longo e duro o suficiente para dar uma pequena dor de cabea.
[Link]()
#
##
###
####
#####
######
#######
string
escrevendo
.length
aps ela.
A maioria dos exerccios contm um pedao de cdigo que pode ser utilizada para alterar e resolver o exerccio.
Lembre-se que voc pode clicar em um bloco de cdigo para edit-lo.
// Your code here.
Dicas:
Voc pode comear com um programa que simplesmente imprime os nmeros de 1 a 7, na qual voc pode
derivar algumas modificaes no exemplo de impresso de nmeros dado no incio do captulo aqui, onde o loop
foi introduzido.
Agora, considere a equivalncia entre nmeros e cadeias em um
adicionando 1 (
+ = 1
hash
FizzBuzz
25
+ = "#"
[Link]()
Fizz
Fizz
Buzz
FizzBuzz
(Isto na verdade uma pergunta de entrevista usada para eliminar uma porcentagem significativa de candidatos
programadores. Ento se voc resolv-la, voc est autorizado de se sentir bem consigo mesmo).
Dica:
Interar sobre os nmeros trabalho claro de um loop, e selecionar o que imprimir uma questo de execuo
condicional. Lembre-se do truque de usar o operador restante (
Na segunda verso o programa tem uma soluo simples e uma inteligente. A maneira mais simples adicionar
um outro "ramo" para um teste preciso da condio dada. Para o mtodo inteligente construir uma sequncia de
caracteres contendo palavra ou palavras para a sada, que imprima a palavra ou o nmero, caso no haja palavra,
fazendo o uso do operador elegante
||
Tabuleiro de Xadrez
Escreva um programa que cria uma
string
que representa uma grade 8x8, usando novas linhas para separar os
caracteres. A cada posio da grade existe um espao ou um caractere "#". Esses caracteres formam um
tabuleiro de xadrez.
Passando esta
string
para o
[Link]
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
Quando voc tiver o programa que gere este padro, defina a varivel
funcione para qualquer
size
size = 8
Dica:
A sequncia pode ser construda iniciando vazia ("") e repetidamente adicionando caracateres. O caracter para
uma nova linha escrito assim
Utilize
[Link]
\n
Para trabalhar com duas dimenses, voc ir precisar de um loop dentro de outro loop. Coloque entre chaves os
"corpos" dos loops para se tornar mais fcil de visualizar quando inicia e quando termina. Tente recuar
adequadamente esses "corpos". A ordem dos loops deve seguir a ordem que usamos para construir a string
(linha por linha, esquerda para direita, cima para baixo). Ento o loop mais externo manipula as linhas e o loop
interno manipula os caracteres por linha.
26
Voc vai precisar de duas variveis para acompanhar seu progresso. Para saber se coloca um espao ou um "#"
em uma determinada posio, voc pode testar se a soma dos dois contadores ainda divisvel por (
% 2
).
Encerrando uma linha com um caracter de nova linha acontece aps a linha de cima ser construda, faa isso
aps o loop interno, mas dentro do loop externo.
27
Funes
As pessoas pensam que Cincia da Computao a arte de gnios. Na realidade o oposto, so vrias
pessoas fazendo coisas que dependem uma das outras, como um muro de pequenas pedras. Donald
Knuth
Voc j viu valores de funes como
alert
JavaScript. O conceito de encapsular uma parte do programa em um valor tem vrios usos. uma ferramenta
usada para estruturar aplicaes de larga escala, reduzir repetio de cdigo, associar nomes a subprogramas e
isolar esses subprogramas uns dos outros.
A aplicao mais bvia das funes quando queremos definir novos vocabulrios. Criar novas palavras no
nosso dia a dia geralmente no uma boa ideia, porm em programao indispensvel.
Um adulto tpico tem por volta de 20.000 palavras em seu vocabulrio. Apenas algumas linguagens de
programao possuem 20.000 conceitos embutidos, sendo que o vocabulrio que se tem disponvel tende a ser
bem definido e, por isso, menos flexvel do que a linguagem usada por humanos. Por isso, normalmente temos
que adicionar conceitos do nosso prprio vocabulrio para evitar repetio.
square
Uma funo criada por meio de uma expresso que se inicia com a palavra-chave
receber uma srie de parmetros (nesse caso, somente
function
. Funes podem
executadas quando a funo for invocada. O "corpo" da funo deve estar sempre envolvido por chaves, mesmo
quando for formado por apenas uma simples declarao (como no exemplo anterior).
Uma funo pode receber mltiplos parmetros ou nenhum parmetro. No exemplo a seguir,
recebe nenhum parmetro, enquanto
power
recebe dois:
28
makeNoise
no
power
square
return
retorno da funo. Quando o controle de execuo interpreta essa declarao, ele sai imediatamente do contexto
da funo atual e disponibiliza o valor retornado para o cdigo que invocou a funo. A palavra-chave
uma expresso aps, ir fazer com que o retorno da funo seja
undefined
return
sem
Parmetros e Escopos
Os parmetros de uma funo comportam-se como variveis regulares. Seu valor inicial informado por quem
invocou a funo e no pelo cdigo da funo em si.
Uma propriedade importante das funes que variveis definidas dentro do "corpo" delas, incluindo seus
parmetros, so locais prpria funo. Isso significa, por exemplo, que a varivel
result
no exemplo
power
ser
criada novamente toda vez que a funo for invocada, sendo que as diferentes execues no interferem umas
nas outras.
Essa caracterstica de localidade das variveis se aplica somente aos parmetros e s variveis que forem
declaradas usando a palavra-chave
var
de alguma funo so chamadas de glob ais (no locais), pois elas so visveis em qualquer parte da aplicao.
possvel acessar variveis glob ais dentro de qualquer funo, contanto que voc no tenha declarado uma
varivel local com o mesmo nome.
O cdigo a seguir demonstra esse conceito. Ele define e executa duas funes em que ambas atribuem um valor
varivel
funo
f2
. A primeira funo
no declara
f1
declara a varivel como local e ento muda apenas seu valor. J a segunda
no topo do exemplo:
29
definida
var x = "outside";
var f1 = function() {
var x = "inside f1";
};
f1();
[Link](x);
// outside
var f2 = function() {
x = "inside f2";
};
f2();
[Link](x);
// inside f2
Esse comportamento ajuda a prevenir interferncias acidentais entre funes. Se todas as variveis fossem
compartilhadas por toda a aplicao, seria muito trabalhoso garantir que o mesmo nome no fosse utilizado em
duas situaes com propsitos diferentes. Alm disso, se fosse o caso de reutilizar uma varivel com o mesmo
nome, talvez voc pudesse se deparar com efeitos estranhos de cdigos que alteram o valor da sua varivel.
Assumindo que variveis locais existem apenas dentro do contexto da funo, a linguagem torna possvel ler e
entender funes como pequenos universos, sem termos que nos preocupar com o cdigo da aplicao inteira
de uma s vez.
Escopo Aninhado
O JavaScript no se distingue apenas pela diferenciao entre variveis locais e glob ais. Funes tambm podem
ser criadas dentro de outras funes, criando vrios nveis de localidades.
Por exemplo, a funo
landscape
flat
mountain
As funes
flat
mountain
result
count
landscape
30
landscape
no consegue ver as
Em resumo, cada escopo local pode tambm ver todos os escopos locais que o contm. O conjunto de variveis
visveis dentro de uma funo determinado pelo local onde aquela funo est escrita na aplicao. Todas as
variveis que estejam em blocos ao redor de definies de funes, so visveis aos corpos dessas funes e
tambm queles que esto no mesmo nvel. Essa abordagem em relao visibilidade de variveis chamada
de escopo lxico.
Pessoas com experincia em outras linguagens de programao podem talvez esperar que qualquer bloco de
cdigo entre chaves produza um novo ambiente local. Entretanto, no JavaScript, as funes so as nicas coisas
que podem criar novos escopos. Tambm permitido a utilizao de blocos livres:
var something = 1;
{
var something = 2;
// Do stuff with variable something...
}
// Outside of the block again...
Entretanto, a varivel
something
embora blocos como esse sejam permitidos, eles so teis somente para agrupar o corpo de uma declarao
condicional
if
ou um lao de repetio.
Se voc acha isso estranho, no se preocupe, pois no est sozinho. A prxima verso do JavaScript vai introduzir
a palavra-chave
let
var
No captulo 5, ns vamos discutir as coisas maravilhosas que podem ser feitas quando passamos valores de
funo para outras funes.
31
. A palavra-chave
function
tambm pode
function square(x) {
return x * x;
}
square
At agora tudo bem, porm existe uma pequena diferena nessa maneira de definir uma funo.
[Link]("The future says:", future());
function future() {
return "We STILL have no flying cars.";
}
O exemplo acima funciona, mesmo sabendo que a funo foi definida aps o cdigo que a executa. Isso ocorre
porque as declaraes de funes no fazem parte do fluxo normal de controle, que executado de cima para
baixo. Elas so conceitualmente movidas para o topo do escopo que as contm e podem ser usadas por
qualquer cdigo no mesmo escopo. Isso pode ser til em algumas situaes, porque nos permite ter a liberdade
de ordenar o cdigo de uma maneira que seja mais expressiva, sem nos preocuparmos muito com o fato de ter
que definir todas as funes antes de us-las.
O que acontece quando definimos uma declarao de funo dentro de um bloco condicional (
if
) ou um lao de
repetio? Bom, no faa isso. Diferentes plataformas JavaScript usadas em diferentes navegadores tm
tradicionalmente feito coisas diferentes nessas situaes, e a ltima verso basicamente probe essa prtica. Se
voc deseja que seu programa se comportem de forma consistente, use somente essa forma de definio de
funo no bloco externo de uma outra funo ou programa.
function example() {
function a() {} // Okay
if (something) {
function b() {} // Danger!
}
}
A Pilha de Chamadas
Ser muito til observamos como o fluxo de controle flui por meio das execues das funes. Aqui, temos um
simples programa fazendo algumas chamadas de funes:
function greet(who) {
[Link]("Hello " + who);
}
greet("Harry");
[Link]("Bye");
[Link]
greet
que assume o controle, faz seu trabalho e ento retorna o controle para a linha 2 novamente. O controle chega ao
fim da funo
greet
e retorna para o local onde a funo foi invocada originalmente (linha 4). Por fim, o controle
[Link]
32
top
greet
[Link]
greet
top
[Link]
top
Devido ao fato de que a funo deve retornar ao local onde foi chamada aps finalizar a sua execuo, o
computador precisa se lembrar do contexto no qual a funo foi invocada originalmente. Em um dos casos,
[Link]
greet
O local onde o computador armazena esse contexto chamado de call stack (pilha de chamadas). Toda vez que
uma funo invocada, o contexto atual colocado no topo dessa "pilha" de contextos. Quando a funo finaliza
sua execuo, o contexto no topo da pilha removido e utilizado para continuar o fluxo de execuo.
O armazenamento dessa pilha de contextos necessita de espao na memria do computador. Quando a pilha
comear a ficar muito grande, o computador reclamar com uma mensagem do tipo out of stack space (sem
espao na pilha) ou too much recursion (muitas recurses). O cdigo a seguir demonstra esse problema fazendo
uma pergunta muito difcil para o computador, que resultar em um ciclo infinito de chamadas entre duas funes.
Se o computador tivesse uma pilha de tamanho infinito, isso poderia ser possvel, no entanto, eventualmente
chegaremos ao limite de espao e explodiremos a "pilha".
function chicken() {
return egg();
}
function egg() {
return chicken();
}
[Link](chicken() + " came first.");
// ??
Argumentos Opcionais
O cdigo abaixo permitido e executa sem problemas:
alert("Hello", "Good Evening", "How do you do?");
A funo
alert
, oficialmente, aceita somente um argumento. No entanto, quando voc a chama assim, ela no
reclama. Ela simplesmente ignora os outros argumentos e lhe mostra o seu "Hello".
O JavaScript extremamente tolerante com a quantidade de argumentos que voc passa para uma funo. Se
voc passar mais argumentos que o necessrio, os extras sero ignorados. Se voc passar menos argumentos,
os parmetros faltantes simplesmente recebero o valor
undefined
A desvantagem disso que, possivelmente - e provavelmente - voc passar um nmero errado de argumentos,
de forma acidental, para as funes e nada ir alert-lo sobre isso.
A vantagem que esse comportamento pode ser usado em funes que aceitam argumentos opcionais. Por
exemplo, a verso seguinte de
power
com apenas um argumento, ela assumir o valor 2 para o expoente e a funo se comportar com um expoente
ao quadrado.
33
No prximo captulo, veremos uma maneira de acessar a lista que contm todos os argumentos que foram
passados para uma funo. Isso til, pois torna possvel uma funo aceitar qualquer nmero de argumentos.
Por exemplo,
[Link]
Closure
A habilidade de tratar funes como valores, combinada com o fato de que variveis locais so recriadas toda vez
que uma funo invocada; isso traz tona uma questo interessante.
O que acontece com as variveis locais quando a funo que as criou no est mais ativa?
O cdigo a seguir mostra um exemplo disso. Ele define uma funo
wrapValue
Isso permitido e funciona como voc espera: a varivel ainda pode ser acessada. Vrias instncias da varivel
podem coexistir, o que uma boa demonstrao do conceito de que variveis locais so realmente recriadas para
cada nova chamada, sendo que as chamadas no interferem nas variveis locais umas das outras.
A funcionalidade capaz de referenciar uma instncia especfica de uma varivel local aps a execuo de uma
funo chamada de closure. Uma funo que closes over (fecha sobre) variveis locais chamada de closure.
Esse comportamento faz com que voc no tenha que se preocupar com o tempo de vida das variveis, como
tambm permite usos criativos de valores de funo.
Com uma pequena mudana, podemos transformar o exemplo anterior, possibilitando a criao de funes que
se multiplicam por uma quantidade arbitrria.
34
function multiplier(factor) {
return function(number) {
return number * factor;
};
}
var twice = multiplier(2);
[Link](twice(5));
// 10
A varivel explcita
localVariable
do exemplo na funo
wrapValue
function
manipulador que possibilita executar instrues computacionais que foram "congeladas" para um uso posterior.
No exemplo,
multiplier
twice
.A
ltima linha do exemplo chama o valor armazenado nessa varivel, fazendo com que o cdigo "congelado" (
number * factor;
multiplier
number
factor
return
e, alm disso, tem acesso ao argumento que foi passado a ele (o valor 5) por meio do parmetro
Recurso
perfeitamente aceitvel uma funo invocar a si mesma, contanto que se tenha cuidado para no sobrecarregar
a pilha de chamadas. Uma funo que invoca a si mesma denominada recursiva. A recursividade permite que
as funes sejam escritas em um estilo diferente. Veja neste exemplo uma implementao alternativa de
power
Essa a maneira mais prxima da forma como os matemticos definem a exponenciao, descrevendo o
conceito de uma forma mais elegante do que a variao que usa um lao de repetio. A funo chama a si
mesma vrias vezes com diferentes argumentos para alcanar a multiplicao repetida.
Entretanto, h um grave problema: em implementaes tpicas no JavaScript, a verso recursiva
aproximadamente dez vezes mais lenta do que a variao que utiliza um lao de repetio. Percorrer um lao de
repetio simples mais rpido do que invocar uma funo mltiplas vezes.
O dilema velocidade versus elegncia bastante interessante. Voc pode interpret-lo como uma forma de
transio gradual entre acessibilidade para humanos e mquina. Praticamente todos os programas podem se
tornar mais rpidos quando se tornam maiores e mais complexos, cabendo ao desenvolvedor decidir qual o
balano ideal entre ambos.
No caso da verso anterior da implementao de
power
bem simples e fcil de ser lida, no fazendo sentido substitu-la pela verso recursiva. Porm, frequentemente
lidamos com aplicaes mais complexas e sacrificar um pouco a eficincia para tornar o cdigo mais legvel e
35
power
programador experiente pode identificar facilmente, que uma abordagem mais simples nunca ser rpida o
suficiente.
A razo pela qual estou enfatizando isso que, surpreendentemente, muitos programadores iniciantes focam
excessivamente em eficincia at nos menores detalhes. Isso acaba gerando programas maiores, mais
complexos e muitas vezes menos corretos, que demoram mais tempo para serem escritos e, normalmente,
executam apenas um pouco mais rapidamente do que as variaes mais simples e diretas.
Porm, muitas vezes a recurso no uma alternativa menos eficiente do que um lao de repetio. muito mais
simples resolver alguns problemas com recurso do que com laos de repetio. A maioria desses problemas
envolve explorao ou processamento de vrias ramificaes, as quais podem se dividir em novas ramificaes e
assim por diante.
Considere este quebra-cabea: iniciando com o nmero 1 e repetidamente adicionando 5 ou multiplicando por 3,
uma infinita quantidade de novos nmeros pode ser produzida. Como voc implementaria uma funo que, dado
um nmero, tenta achar a sequncia de adies e multiplicaes que produzem esse nmero? Por exemplo, o
nmero 13 pode ser produzido multiplicando-se por 3 e adicionando-se 5 duas vezes. J o nmero 15 no pode
ser produzido de nenhuma forma.
Aqui est uma soluo recursiva:
function findSolution(target) {
function find(start, history) {
if (start == target)
return history;
else if (start > target)
return null;
else
return find(start + 5, ( + history + + 5)) ||
find(start * 3, ( + history + * 3));
}
return find(1, 1);
}
[Link](findSolution(24));
// (((1 * 3) + 5) * 3)
Note que esse programa no necessariamente encontra a menor sequncia de operaes. Ele termina sua
execuo quando encontra a primeira soluo possvel.
Eu no espero que voc entenda como isso funciona imediatamente, mas vamos analisar o exemplo, pois um
timo exerccio para entender o pensamento recursivo.
A funo interna
find
responsvel pela recurso. Ela recebe dois argumentos (o nmero atual e uma string que
registra como chegamos a esse nmero) e retorna uma string que mostra como chegar no nmero esperado ou
null
Para fazer isso, a funo executa uma entre trs aes possveis. Se o nmero atual o nmero esperado, o
histrico atual reflete uma possvel sequncia para alcanar o nmero esperado, ento ele simplesmente
retornado. Se o nmero atual maior que o nmero esperado, no faz sentido continuar explorando o histrico, j
36
que adicionar ou multiplicar o nmero atual gerar um nmero ainda maior. Por fim, se ns tivermos um nmero
menor do que o nmero esperado, a funo tentar percorrer todos os caminhos possveis que iniciam do
nmero atual, chamando ela mesma duas vezes, uma para cada prximo passo que seja permitido. Se a
primeira chamada retornar algo que no seja
null
null
Para entender melhor como essa funo produz o resultado que estamos esperando, vamos analisar todas as
chamadas a
find
find(1, 1)
find(6, (1 + 5))
find(11, ((1 + 5) + 5))
find(16, (((1 + 5) + 5) + 5))
too big
find(33, (((1 + 5) + 5) * 3))
too big
find(18, ((1 + 5) * 3))
too big
find(3, (1 * 3))
find(8, ((1 * 3) + 5))
find(13, (((1 * 3) + 5) + 5))
found!
(1 + 5)
(1 + 5)
(1 * 3)
find
menor ou igual ao nmero esperado. Como ele no encontra uma soluo para o nmero esperado, o valor
retornado at retornar para a chamada inicial. Nesse momento, o operador
chamadas inicie o processo de explorao pelo outro caminho
(1 * 3)
||
null
porque aps duas chamadas recursivas acaba encontrando o nmero 13. Essa chamada recursiva mais interna
retorna uma
string
e cada operador
||
string
adiante, retornando
Funes Crescentes
Existem duas razes naturais para as funes serem introduzidas nos programas.
A primeira delas quando voc percebe que est escrevendo o mesmo cdigo vrias vezes. Ns queremos evitar
isso, pois quanto mais cdigo, maiores so as chances de erros e mais linhas de cdigo h para as pessoas
lerem e entenderem o programa. Por isso, ns extramos a funcionalidade repetida, encontramos um bom nome
para ela e colocamos dentro de uma funo.
A segunda razo quando voc precisa de uma funcionalidade que ainda no foi escrita e que merece ser
encapsulada em uma funo prpria. Voc comea dando um nome funo e, em seguida, escreve o seu corpo.
s vezes, voc pode at comear escrevendo o cdigo que usa a funo antes mesmo de defini-la.
A dificuldade de encontrar um bom nome para uma funo um bom indicativo de quo claro o conceito que
voc est tentando encapsular. Vamos analisar um exemplo.
Ns queremos escrever um programa que imprima dois nmeros, sendo eles o nmero de vacas e galinhas em
uma fazenda com as palavras Cows (vacas) e Chickens (galinhas) depois deles e algarismos zeros antes de
ambos os nmeros para que sejam sempre nmeros de trs dgitos.
007 Cows
011 Chickens
37
Bom, claramente, isso uma funo que exige dois argumentos. Vamos codar.
function printFarmInventory(cows, chickens) {
var cowString = String(cows);
while ([Link] < 3)
cowString = 0 + cowString;
[Link](cowString + Cows);
var chickenString = String(chickens);
while ([Link] < 3)
chickenString = 0 + chickenString;
[Link](chickenString + Chickens);
}
printFarmInventory(7, 11);
Adicionar
string
.length
string
while
string
que representa o
printZeroPaddedWithLabel
38
zeroPad
Alm disso, ele pode ser til em outras situaes, alm desse programa especfico. Voc pode us-lo, por
exemplo, para imprimir nmeros corretamente alinhados em uma tabela.
O quo inteligente e verstil as nossas funes deveriam ser? Ns poderamos escrever funes extremamente
simples, que apenas adicionam algarismos para que o nmero tenha trs caracteres, at funes complicadas,
para formatao de nmeros fracionrios, nmeros negativos, alinhamento de casas decimais, formatao com
diferentes caracteres e por a vai.
Um princpio til no adicionar funcionalidades, a menos que voc tenha certeza absoluta de que ir precisar
delas. Pode ser tentador escrever solues genricas para cada funcionalidade com que voc se deparar.
Resista a essa vontade. Voc no vai ganhar nenhum valor real com isso e vai acabar escrevendo muitas linhas
de cdigo que nunca sero usadas.
printZeroPaddedWithLabel
zeroPad
coincidncia que a segunda verso til em mais situaes do que a primeira. Funes que criam valores so
mais fceis de serem combinadas de diferentes maneiras do que funes que produzem efeitos colaterais
diretamente.
Uma funo "pura" um tipo especfico de funo que produz valores e que no gera efeitos colaterais, como
tambm no depende de efeitos colaterais de outros cdigos por exemplo, ela no utiliza variveis globais que
podem ser alteradas por outros cdigos. Uma funo pura tem a caracterstica de, ser sempre chamada com os
mesmos argumentos, produzir o mesmo valor (e no far nada alm disso). Isso acaba fazendo com que seja
fcil de entendermos como ela funciona. Uma chamada para tal funo pode ser mentalmente substituda pelo
seu resultado, sem alterar o significado do cdigo. Quando voc no tem certeza se uma funo pura est
funcionando corretamente, voc pode test-la simplesmente invocando-a. Sabendo que ela funciona nesse
contexto, funcionar em qualquer outro contexto. Funes que no so "puras" podem retornar valores diferentes
baseados em vrios tipos de fatores e produzem efeitos colaterais que podem fazer com que seja difcil de testar
e pensar sobre elas.
Mesmo assim, no h necessidade de se sentir mal ao escrever funes que no so "puras" ou comear uma
"guerra santa" para eliminar cdigos impuros. Efeitos colaterais so teis em algumas situaes. No existe uma
verso "pura" de
[Link]
, por exemplo, e
[Link]
fceis de se expressar de forma mais eficiente quando usamos efeitos colaterais, portanto a velocidade de
computao pode ser uma boa razo para se evitar a "pureza".
Resumo
Este captulo ensinou a voc como escrever suas prprias funes. A palavra-chave
function
, quando usada
como uma expresso, pode criar um valor de funo. Quando usada como uma declarao, pode ser usada para
declarar uma varivel e dar a ela uma funo como valor.
39
Um aspecto chave para entender funes, entender como os escopos locais funcionam. Parmetros e variveis
declaradas dentro de uma funo so locais quela funo, recriados toda vez que a funo invocada, e no so
acessveis do contexto externo funo. Funes declaradas dentro de outras tm acesso ao escopo local das
funes mais externas que as envolvem.
Separar as tarefas que a sua aplicao executa em diferentes funes, bastante til. Voc no ter que repetir o
cdigo e as funes fazem um programa mais legvel, agrupando o cdigo em pedaos conceituais, da mesma
forma que os captulos e as sees ajudam a organizar um texto.
Exerccios
Mnimo
O captulo anterior introduziu a funo
[Link]
min
Dica: Se estiver tendo problemas para colocar as chaves e os parnteses nos seus lugares corretos, para ter
uma definio de uma funo vlida, comece copiando um dos exemplos desse captulo e modificando-o. Uma
funo pode conter vrias declaraes de retorno (
return
).
Recurso
Ns vimos que o
(operador resto) pode ser usado para testar se um nmero par ou mpar, usando
% 2
para
verificar se ele divisvel por dois. Abaixo, est uma outra maneira de definir se um nmero inteiro positivo par
ou mpar:
Zero par.
Um mpar.
Para todo outro nmero N, sua paridade a mesma de N - 2.
Defina uma funo recursiva
isEven
40
if
else if
else
find
do exemplo recursivo
findSolution
final,
correspondente ao terceiro caso, responsvel por fazer a chamada recursiva. Cada uma das ramificaes
dever conter uma declarao de retorno ou retornar um valor especfico.
Quando o argumento recebido for um nmero negativo, a funo ser chamada recursivamente vrias vezes,
passando para si mesma um nmero cada vez mais negativo, afastando-se cada vez mais de retornar um
resultado. Ela, eventualmente, consumir todo o espao em memria da pilha de chamadas e abortar.
Contando feijes
Voc pode acessar o N-simo caractere, ou letra, de uma
voc acessa seu tamanho com
(por exemplo,
posio
"b"
"s".length
string
escrevendo
string
"string".charAt(N)
, similar a como
). O primeiro caractere est na posio zero, o que faz com que o ltimo seja encontrado na
[Link] -1
countBs
string
length
) dois, e
string
countChar
string
countBs
, exceto
que ela recebe um segundo argumento que indica o caractere que ser contado (ao invs de contar somente o
caractere "B" em maisculo). Reescreva
countBs
Dica: Um lao de repetio em sua funo far com que todos os caracteres na
usarmos um ndice de zero at uma unidade abaixo que seu tamanho (
string
< [Link]
sejam verificados se
). Se o caractere na
posio atual for o mesmo que a funo est procurando, ele incrementar uma unidade na varivel de contagem
(
counter
counter
41
var
O Esquilo-homem
De vez em quando, geralmente entre oito e dez da noite, Jacques se transforma em um pequeno roedor peludo
com uma cauda espessa.
Por um lado, Jacques fica muito contente por no ter licantropia clssica. Transformar-se em um esquilo tende a
causar menos problemas do que se transformar em um lobo. Ao invs de ter que se preocupar em comer
acidentalmente o vizinho (isso seria bem estranho), ele tem que se preocupar em no ser comido pelo gato do
vizinho. Aps duas ocasies em que ele acordou nu, desorientado e em cima de um galho fino na copa de uma
rvore, ele resolveu trancar as portas e as janelas do seu quarto durante a noite e colocar algumas nozes no cho
para manter-se ocupado.
Isso resolve os problemas do gato e da rvore. Mesmo assim, Jacques ainda sofre com sua condio. As
ocorrncias irregulares das transformaes o faz suspeitar de que talvez possa ter alguma coisa que as ativam.
Por um tempo, ele acreditava que isso s acontecia nos dias em que ele havia tocado em rvores. Por isso, ele
parou de tocar de vez nas rvores e at parou de ficar perto delas, mas o problema persistiu.
42
Mudando para uma abordagem mais cientfica, Jacques pretende comear a manter um registro dirio de tudo o
que ele faz e se ele se transformou. Com essas informaes, ele espera ser capaz de diminuir e limitar as
condies que ativam as transformaes.
A primeira coisa que ele dever fazer criar uma estrutura de dados para armazenar essas informaes.
Conjuntos de dados
Para trabalhar com um pedao de dados digitais, primeiramente precisamos encontrar uma maneira de
represent-los na memria da nossa mquina. Vamos dizer que, como um exemplo simples, queremos
representar a coleo de nmeros: 2, 3, 5, 7 e 11.
Poderamos ser criativos com strings (elas podem ter qualquer tamanho, assim podemos armazenar muitos
dados nelas) e usar
"2 3 5 7 11"
como nossa representao. Entretanto, isso estranho, pois voc teria que, de
A notao para acessar elementos contidos em um array tambm usa colchetes. Um par de colchetes
imediatamente aps uma expresso, contendo outra expresso entre esses colchetes, ir procurar o elemento
contido na expresso esquerda que est na posio dada pelo ndice determinado pela expresso entre
colchetes.
Indexao de Arrays
O primeiro ndice de um array o nmero zero e no o nmero um. Portanto, o primeiro elemento pode ser
acessado usando
listOfNumbers[0]
um tempo para se acostumar. Mesmo assim, a contagem baseada em zero uma tradio de longa data no
mundo da tecnologia e, desde que seja seguida consistentemente (que o caso no JavaScript), ela funciona
bem.
Propriedades
Ns vimos, em exemplos anteriores, algumas expresses de aparncia suspeita, como
acessar o tamanho de uma string) e
[Link]
myString
[Link]
(para
max
no objeto
Math
length
do valor contido
(que um conjunto de
43
null
undefined
As duas formas mais comuns de acessar propriedades no JavaScript so usando ponto e colchetes. Ambos
value.x
value[x]
value
interpretado. Quando usamos o ponto, a parte aps o ponto deve ser um nome de
varivel vlido e referente ao nome da propriedade em questo. Quando usamos colchetes, a expresso entre os
colchetes avaliada para obter o nome da propriedade. Enquanto que
"x",
value[x]
value.x
Portanto, se voc sabe que a propriedade que voc est interessado se chama "length", voc usa
Se voc deseja extrair a propriedade cujo nome o valor que est armazenado na varivel
[Link]
, voc usa
value[i]
Devido ao fato de que nomes de propriedades podem ser qualquer string, se voc quiser acessar as
propriedades "2" ou "John Doe", voc deve usar os colchetes:
ou
value[2]
value["John Doe"]
, pois mesmo
sabendo exatamente o nome da propriedade, "2" e "John Doe" no so nomes vlidos de variveis, sendo
impossvel acess-los usando a notao com o ponto.
Mtodos
Ambos os objetos string e array possuem, alm da propriedade
length
toUpperCase
toLowerCase
que faz.
Curiosamente, mesmo que a chamada para
funo tem acesso string
"Doh"
toUpperCase
toUpperCase
O exemplo a seguir demonstra alguns mtodos que os objetos do tipo array possuem:
var mack = [];
[Link]("Mack");
[Link]("the", "Knife");
[Link](mack);
// ["Mack", "the", "Knife"]
[Link]([Link](" "));
// Mack the Knife
[Link]([Link]());
// Knife
[Link](mack);
// ["Mack", "the"]
O mtodo
push
pop
o valor que est no final do array e o retorna. Um array de strings pode ser combinado em uma nica string com o
mtodo
join
join
array.
44
Objetos
Voltando ao esquilo-homem. Um conjunto de registros dirios pode ser representado como um array. Entretanto,
as entradas no so compostas apenas por um nmero ou uma string, pois precisam armazenar a lista de
atividades e um valor booleano que indica se Jacques se transformou em um esquilo. Ns deveramos,
idealmente, agrupar esses valores em um nico valor e, em seguida, coloc-los em um array com os registros.
Valores do tipo ob jeto so colees arbitrrias de propriedades, sendo que podemos adicionar ou remover essas
propriedades da forma que desejarmos. Uma maneira de criar um objeto usando a notao com chaves.
var day1 = {
squirrel: false,
events: ["work", "touched tree", "pizza", "running",
"television"]
};
[Link]([Link]);
// false
[Link]([Link]);
// undefined
[Link] = false;
[Link]([Link]);
// false
Dentro das chaves, podemos informar uma lista de propriedades separadas por vrgulas. Cada propriedade
escrita com um nome seguido de dois pontos e uma expresso que fornece o valor da propriedade. Espaos e
quebras de linha no fazem diferena. Quando um objeto se estende por vrias linhas, indent-lo, como mostrado
no exemplo anterior, melhora a legibilidade. Propriedades cujos nomes no so variveis ou nmeros vlidos
precisam estar entre aspas.
var descriptions = {
work: "Went to work",
"touched tree": "Touched a tree"
};
Isso significa que as chaves possuem dois significados no JavaScript. Quando usadas no incio de uma
declarao, elas definem o comeo de um bloco de declaraes. Em qualquer outro caso, elas descrevem um
objeto. Felizmente, praticamente intil iniciar uma declarao com as chaves de um objeto e, em programas
normais, no existe ambiguidade entre os dois casos de uso.
Tentar acessar uma propriedade que no existe ir produzir um valor
ler pela primeira vez a propriedade
wolf
undefined
no exemplo anterior.
propriedade, caso ela exista, ou criar uma nova propriedade no objeto se no existir.
Retornando brevemente ao nosso modelo de tentculos para associaes de variveis, as associaes de
propriedades funcionam de forma similar. Elas receb em valores, mas outras variveis e propriedades podem
tambm estar associadas aos mesmos valores. Voc pode pensar em objetos como polvos com um nmero
qualquer de tentculos, e cada tentculo com um nome escrito nele.
45
O operador
delete
corta um tentculo de nosso polvo. Ele um operador unrio que, quando aplicado a uma
propriedade, ir remover tal propriedade do objeto. Isso no algo comum de se fazer, mas possvel.
var anObject = {left: 1, right: 2};
[Link]([Link]);
// 1
delete [Link];
[Link]([Link]);
// undefined
[Link]("left" in anObject);
// false
[Link]("right" in anObject);
// true
O operador binrio
in
, quando aplicado a uma string ou a um objeto, retorna um valor booleano que indica se
aquele objeto possui aquela propriedade. A diferena entre alterar uma propriedade para
undefined
e remov-la
de fato, que no primeiro caso, o objeto continua com a propriedade (ela simplesmente no tem um valor muito
interessante), enquanto que no segundo caso, a propriedade no estar mais presente no objeto e o operador
in
retornar
false
Os arrays so, ento, apenas um tipo especializado de objeto para armazenar sequncias de coisas. Se voc
executar
typeof [1, 2]
, ir produzir
"object"
46
Mutabilidade
Ns iremos chegar na programao de fato muito em breve. Mas h, primeiramente, uma ltima parte terica que
precisamos entender.
Ns vimos que os valores de objetos podem ser modificados. Os tipos de valores discutidos nos captulos
anteriores, tais como nmeros, strings e booleanos, so imutveis. impossvel mudar o valor j existente
desses tipos. Voc pode, a partir deles, combin-los e criar novos valores, mas quando voc analisar um valor
especfico de string, ele ser sempre o mesmo, sendo que o seu texto no pode ser alterado. Por exemplo, se
voc tiver referncia a uma string que contm
string para
"rat"
"cat"
Por outro lado, no caso de objetos, o contedo de um valor pode ser modificado quando alteramos suas
propriedades.
Quando temos dois nmeros, 120 e 120, podemos consider-los exatamente os mesmos nmeros, mesmo se
eles no fazem referncia aos mesmos bits fsicos. Entretanto, no caso de objetos h uma diferena entre ter
duas referncias para o mesmo objeto e ter dois objetos diferentes que possuem as mesmas propriedades.
Considere o cdigo a seguir:
var object1 = {value: 10};
var object2 = object1;
var object3 = {value: 10};
[Link](object1 == object2);
// true
[Link](object1 == object3);
// false
[Link] = 15;
[Link]([Link]);
// 15
[Link]([Link]);
// 10
As variveis
valor de
object1
object2
object2
. A varivel
propriedades de
object1
object3
object1
tambm altera o
==
do JavaScript ir retornar
true
false
contedos idnticos. No existe uma operao nativa no JavaScript de "deep" comparison (comparao
47
"profunda"), onde se verifica o contedo de um objeto, mas possvel escrev-la voc mesmo (que ser um dos
exerccios ao final desse captulo).
O log da licantropia
Jacques inicia seu interpretador de JavaScript e configura o ambiente que ele precisa para manter o seu dirio.
var journal = [];
function addEntry(events, didITurnIntoASquirrel) {
[Link]({
events: events,
squirrel: didITurnIntoASquirrel
});
}
E ento, todas as noites s dez ou as vezes na manh seguinte aps descer do topo de sua estante de livros, ele
faz o registro do dia.
addEntry(["work", "touched tree", "pizza", "running",
"television"], false);
addEntry(["work", "ice cream", "cauliflower", "lasagna",
"touched tree", "brushed teeth"], false);
addEntry(["weekend", "cycling", "break", "peanuts",
"beer"], true);
Uma vez que ele tem dados suficientes, ele pretende calcular a correlao entre sua transformao em esquilo e
cada um dos eventos do dia e espera aprender algo til a partir dessas correlaes.
A correlao uma medida de dependncia entre variveis ("variveis" no sentido estatstico e no no sentido do
JavaScript). Ela geralmente expressa em um coeficiente que varia de -1 a 1. Zero correlao significa que as
variveis no so relacionadas, enquanto que a correlao de um indica que as variveis so perfeitamente
relacionadas (se voc conhece uma, voc tambm conhece a outra). A correlao negativa de um tambm indica
que as variveis so perfeitamente relacionadas, mas so opostas (quando uma verdadeira, a outra falsa).
Para variveis binrias (booleanos), o coeficiente phi () fornece uma boa forma de medir a correlao e
relativamente fcil de ser calculado. Para calcular , precisamos de uma tabela n que contm o nmero de vezes
que as diversas combinaes das duas variveis foram observadas. Por exemplo, podemos considerar o evento
de "comer pizza" e coloc-lo nessa tabela da seguinte maneira:
48
A notao n01 indica o nmero de ocorrncias nas quais a primeira varivel (transformar-se em esquilo) falsa
(0) e a segunda varivel (pizza) verdadeira (1). Nesse exemplo, n01 igual a 9.
O valor n1 se refere soma de todas as medidas nas quais a primeira varivel verdadeira, que no caso do
exemplo da tabela 5. Da mesma forma, n0 se refere soma de todas as medidas nas quais a segunda
varivel falsa.
Portanto, para a tabela de pizza, a parte de cima da linha (o dividendo) seria 1x76 - 4x9 = 40, e a parte de baixo (o
divisor) seria a raiz quadrada de 5x85x10x80, ou 340000. Esse clculo resulta em 0.069, o que um valor
bem pequeno. Comer pizza parece no ter influncia nas transformaes.
Calculando a correlao
No JavaScript, podemos representar uma tabela dois por dois usando um array com quatro elementos (
1]
[76, 9, 4,
). Podemos tambm usar outras formas de representaes, como por exemplo um array contendo dois arrays
"11"
. Entretanto, a maneira mais simples e que faz com que seja mais fcil acessar os dados utilizando um
array com quatro elementos. Ns iremos interpretar os ndices do array como elementos binrios de dois bits,
onde o dgito a esquerda (mais significativo) se refere varivel do esquilo, e o dgito a direita (menos
significativo) se refere varivel do evento. Por exemplo, o nmero binrio
10
se tornou um esquilo, mas o evento no ocorreu (por exemplo "pizza"). Isso aconteceu quatro vezes, e j que o
nmero binrio
10
array.
Essa a funo que calcula o coeficiente de um array desse tipo:
49
function phi(table) {
return (table[3] * table[0] - table[2] * table[1]) /
[Link]((table[2] + table[3]) *
(table[0] + table[1]) *
(table[1] + table[3]) *
(table[0] + table[2]));
}
[Link](phi([76, 9, 4, 1]));
// 0.068599434
Math
[Link]
para encontrar valores como n1, pois a soma das linhas ou colunas no so armazenadas diretamente em
nossa estrutura de dados.
Jacques manteve seu dirio por trs meses. O conjunto de dados resultante est disponvel no ambiente de
cdigo desse captulo, armazenado na varivel
JOURNAL
Para extrair uma tabela dois por dois de um evento especfico desse dirio, devemos usar um loop para percorrer
todas as entradas e ir adicionando quantas vezes o evento ocorreu em relao s transformaes de esquilo.
function hasEvent(event, entry) {
return [Link](event) != -1;
}
function tableFor(event, journal) {
var table = [0, 0, 0, 0];
for (var i = 0; i < [Link]; i++) {
var entry = journal[i], index = 0;
if (hasEvent(event, entry)) index += 1;
if ([Link]) index += 2;
table[index] += 1;
}
return table;
}
[Link](tableFor("pizza", JOURNAL));
// [76, 9, 4, 1]
A funo
indexOf
hasEvent
que procura pelo valor informado no array (nesse exemplo o nome do evento), e retorna o ndice onde ele
indexOf
encontrado.
O corpo do loop presente na funo
tableFor
verificando se essa entrada contm o evento especfico e se o evento ocorreu juntamente com um incidente de
transformao em esquilo. O loop adiciona uma unidade no nmero contido no array que corresponde a essa
caixa na tabela.
Agora temos as ferramentas necessrias para calcular correlaes individuais. O nico passo que falta
encontrar a correlao para cada tipo de evento que foi armazenado e verificar se algo se sobressai. Como
podemos armazenar essas correlaes assim que as calculamos?
50
Uma maneira possvel armazenar todas as correlaes em um array, usando objetos com as propriedades
name
(nome) e
value
(valor). Porm, isso faz com que o acesso s correlaes de um evento seja bastante
trabalhoso, pois voc teria que percorrer por todo o array para achar o objeto com o
name
certo. Poderamos
encapsular esse processo de busca em uma funo e, mesmo assim, iramos escrever mais cdigo e o
computador iria trabalhar mais do que o necessrio.
Uma maneira melhor seria usar as propriedades do objeto nomeadas de acordo com o tipo do evento. Podemos
usar a notao de colchetes para acessar e ler as propriedades e, alm disso, usar o operador
in
para testar se
Um map uma maneira de associar valores de um domnio (nesse caso nomes de eventos) com seus valores
correspondentes em outro domnio (nesse caso coeficientes ).
Existem alguns problemas que podem ser gerados usando objetos dessa forma, os quais sero discutidos no
captulo 6. Por enquanto, no iremos nos preocupar com eles.
E se quisssemos encontrar todos os eventos nos quais armazenamos um coeficiente? Diferentemente de um
array, as propriedades no formam uma sequncia previsvel, impossibilitando o uso de um loop
for
normal.
Entretanto, o JavaScript fornece uma construo de loop especfica para percorrer as propriedades de um objeto.
Esse loop parecido com o loop
for
in
A anlise final
Para achar todos os tipos de eventos que esto presentes no conjunto de dados, ns simplesmente
processamos cada entrada e percorremos por todos os eventos presentes usando um loop. Mantemos um objeto
chamado
phis
que contm os coeficientes das correlaes de todos os tipos de eventos que foram vistos at
agora. A partir do momento em que encontramos um tipo que no est presente no objeto
valor de sua correlao e ento adicionamos ao objeto.
51
phis
, calculamos o
function gatherCorrelations(journal) {
var phis = {};
for (var entry = 0; entry < [Link]; entry++) {
var events = journal[entry].events;
for (var i = 0; i < [Link]; i++) {
var event = events[i];
if (!(event in phis))
phis[event] = phi(tableFor(event, journal));
}
}
return phis;
}
var correlations = gatherCorrelations(JOURNAL);
[Link]([Link]);
// 0.068599434
0.0140970969
// exercise: 0.0685994341
// weekend:
// bread:
0.1371988681
-0.0757554019
// pudding: -0.0648203724
// and so on...
A grande maioria das correlaes tendem a zero. Comer cenouras, po ou pudim aparentemente no ativam a
transformao em esquilo. Entretanto, elas parecem acontecer com mais frequncia aos finais de semana.
Vamos filtrar os resultados para mostrar apenas as correlaes que so maiores do que 0.1 ou menores do que
-0.1.
for (var event in correlations) {
var correlation = correlations[event];
if (correlation > 0.1 || correlation < -0.1)
[Link](event + ": " + correlation);
}
// weekend:
0.1371988681
0.1296407447
// work:
-0.1371988681
// spaghetti:
0.2425356250
// reading:
0.1106828054
// peanuts:
0.5902679812
A-ha! Existem dois fatores nos quais a correlao claramente mais forte que a das outras. Comer amendoins
tem um forte efeito positivo na chance de se transformar em um esquilo, enquanto que escovar os dentes tem um
significante efeito negativo.
Interessante. Vamos tentar uma coisa.
for (var i = 0; i < [Link]; i++) {
var entry = JOURNAL[i];
if (hasEvent("peanuts", entry) &&
!hasEvent("brushed teeth", entry))
[Link]("peanut teeth");
}
[Link](phi(tableFor("peanut teeth", JOURNAL)));
// 1
52
Est bem evidente! O fenmeno ocorre precisamente quando Jacques come amendoins e no escova os dentes.
Se ele no fosse preguioso em relao higiene bucal, ele no sequer teria reparado nesse problema que o
aflige.
Sabendo disso, Jacques simplesmente para de comer amendoins e descobre que isso coloca um fim em suas
transformaes.
Tudo ficou bem com Jacques por um tempo. Entretanto, alguns anos depois, ele perdeu seu emprego e
eventualmente foi forado a trabalhar em um circo, onde suas performances como O Incrvel Homem-Esquilo se
baseavam em encher sua boca com pasta de amendoim antes de cada apresentao. Em um dia de sua pobre
existncia, Jacques no conseguiu se transformar de volta em sua forma humana e fugiu do circo, desapareceu
pela floresta e nunca mais foi visto.
push
pop
unshift
O programa anterior gerencia uma lista de tarefas. Voc pode adicionar tarefas no final da lista chamando
rememberTo("eat")
urgentlyRememberTo
whatIsNext()
para acessar
indexOf
lastIndexOf
Ambos
indexOf
lastIndexOf
slice
contm apenas os elementos presentes entre esses ndices. O ndice de incio inclusivo e o de parada
exclusivo.
[Link]([0, 1, 2, 3, 4].slice(2, 4));
// [2, 3]
[Link]([0, 1, 2, 3, 4].slice(2));
// [2, 3, 4]
53
concat
slice
slice
pode ser usado para unir arrays, parecido com o que o operador
concat
slice
retornando um novo array que uma cpia do array original, exceto pelo fato de que o elemento no ndice
informado foi removido.
function remove(array, index) {
return [Link](0, index)
.concat([Link](index + 1));
}
[Link](remove(["a", "b", "c", "d", "e"], 2));
// ["a", "b", "d", "e"]
length
toUpperCase
Valores do tipo string, numb er e Boolean no so objetos e, mesmo pelo fato da linguagem no reclamar quando
tentamos adicionar novas propriedades neles, elas no so armazenadas. Esses valores so imutveis e no
podem ser alterados.
Mesmo assim, esses tipos possuem propriedades nativas. Toda string possui uma srie de mtodos.
Provavelmente, alguns dos mais teis so
slice
indexOf
indexOf
das strings pode receber uma string contendo mais de um caractere, enquanto
O mtodo
trim
remove todos os espaos vazios (espaos, linhas, tabs e caracteres similares) do comeo e do
okay \n ".trim());
// okay
J vimos a propriedade
mtodo
charAt
length
das strings. Para acessar caracteres individuais de uma string, podemos usar o
ou simplesmente ler suas propriedades numricas, da mesma forma que voc faria em um array.
54
O Objeto Arguments
Sempre que uma funo invocada, uma varivel especial chamada
arguments
o corpo da funo executa. Essa varivel se refere a um objeto que contm todos os argumentos passados
funo. Lembre-se de que no JavaScript voc pode passar mais (ou menos) argumentos para uma funo,
independentemente do nmero de parmetros que foi declarado.
function noArguments() {}
noArguments(1, 2, 3); // This is okay
function threeArguments(a, b, c) {}
threeArguments(); // And so is this
O objeto
arguments
possui a propriedade
length
passado funo. Alm disso, contm uma propriedade para cada argumento, chamadas 0, 1, 2, etc.
Se isso soa muito parecido como um array para voc, voc est certo. Esse objeto muito parecido com um
array. Porm, ele no possui nenhum dos mtodos de array (como
slice
ou
indexOf
arguments
[Link]
. Essas funes
extremamente agradveis. Por exemplo, lembre-se de como criamos as entradas no dirio do Jacques.
addEntry(["work", "touched tree", "pizza", "running",
"television"], false);
Devido ao fato de que essa funo ir ser executada muitas vezes, poderamos criar uma alternativa mais
simples.
function addEntry(squirrel) {
var entry = {events: [], squirrel: squirrel};
for (var i = 1; i < [Link]; i++)
[Link](arguments[i]);
[Link](entry);
}
addEntry(true, "work", "touched tree", "pizza",
"running", "television");
squirrel
55
O Objeto Math
Como vimos anteriormente,
(mximo),
[Link]
O objeto
Math
[Link]
Math
(mnimo) e
[Link]
(raiz quadrada).
usado como um container para agrupar uma srie de funcionalidades relacionadas. Existe
Math
e, na maioria das vezes, ele no til quando usado como um valor. Mais
precisamente, ele fornece um namespace (espao nominal) para que todas essas funes e valores no
precisem ser declaradas como variveis globais.
Possuir muitas variveis globais "polui" o namespace. Quanto mais nomes so usados, mais provveis so as
chances de acidentalmente sobrescrever o valor de uma varivel. Por exemplo, provvel que voc queira chamar
algo de
max
Math
max
Muitas linguagens iro parar voc ou, ao menos, avis-lo quando tentar definir uma varivel com um nome que j
est sendo usado. Como o JavaScript no faz isso, tenha cuidado.
De volta ao objeto
(cosseno),
sin
Math
(seno) e
tan
Math
aos
asin
cos
atan
respectivamente. O nmero (pi), ou pelo menos a aproximao que possvel ser representada atravs de um
nmero no JavaScript, est disponvel como
[Link]
Se senos e cossenos no so muito familiares para voc, no se preocupe. Quando eles forem usados no
Captulo 13 desse livro, eu lhe explicarei.
O exemplo anterior usa
[Link]
Embora os computadores sejam deterministas (sempre reagem da mesma maneira quando so usados os
mesmos dados de entrada), possvel fazer com que eles produzam nmeros que paream ser aleatrios. Para
fazer isso, a mquina mantm um nmero (ou uma quantidade deles) armazenado em seu estado interno.
Assim, toda vez que um nmero aleatrio requisitado, ela executa alguns clculos complicados e deterministas
usando esse estado interno e, ento, retorna parte do resultado desses clculos. A mquina tambm utiliza
esses resultados para mudar o seu estado interno, fazendo com que o prximo nmero "aleatrio" produzido seja
diferente.
Se ao invs de um nmero fracionrio, quisermos um nmero aleatrio inteiro, podemos usar
arredonda o nmero para o menor valor inteiro mais prximo) no resultado de
56
[Link]
[Link]
(que
[Link]([Link]([Link]() * 10));
// 2
Multiplicar o nmero aleatrio por dez resulta em um nmero que seja maior ou igual a zero e menor do que dez.
Devido ao fato de que
[Link]
arredonda o valor para baixo, essa expresso ir produzir, com chances iguais,
[Link]
O objeto global
O escopo global, que o espao no qual as variveis globais residem, tambm pode ser abordado como um
objeto no JavaScript. Cada varivel global est presente como uma propriedade desse objeto. Nos navegadores,
o objeto do escopo global armazenado na varivel
window
Resumo
Objetos e arrays (que so tipos especficos de objetos) fornecem maneiras de agrupar uma conjunto de valores
em um nico valor. Conceitualmente, ao invs de tentar carregar e manter todas as coisas individualmente em
nossos braos, eles nos permitem colocar e carregar todas as coisas relacionadas dentro de uma bolsa.
Com exceo de
usando
null
[Link]
undefined
ou
value["propName"]
armazenam mais o menos uma quantidade fixa delas. Por outro lado, os Arrays normalmente contm
quantidades variveis de valores conceitualmente iguais e usam nmeros (iniciando do zero) como os nomes de
suas propriedades.
Existem algumas propriedades com nomes especficos nos arrays, como
length
Mtodos so funes que so armazenadas em propriedades e, normalmente, atuam no valor nas quais elas
so propriedade.
Objetos podem tambm ser usados como mapas, associando valores com seus nomes. O operador
in
pode
ser usado para verificar se um objeto contm a propriedade com o nome informado. A mesma palavra-chave pode
ser usada em um loop
for
Exerccios
A soma de um intervalo
A introduo desse livro mencionou a seguinte maneira como uma boa alternativa para somar um intervalo de
nmeros:
[Link](sum(range(1, 10)));
57
range
start
at o valor
end
(incio) e
start
end
(incluindo-o).
sum
range
tamanho do "incremento" usado para construir o array. Se nenhum valor for atribudo ao tamanho do incremento, o
array de elementos ser percorrido em incrementos de um, correspondendo ao comportamento original. A
chamada funo
range(1, 10, 2)
deve retornar
[1, 3, 5, 7, 9]
range(5, 2, -1)
produza
[5, 4, 3, 2]
Dicas
A maneira mais fcil de construir um array primeiramente inicializar uma varivel usando
vazio) e, em seguida, chamar vrias vezes o seu mtodo
push
[]
<=
<
Para verificar se o argumento opcional de incremento foi fornecido, voc pode verificar o
comparar o valor do argumento com
undefined
[Link]
ou
range
entenda incrementos negativos provavelmente mais fcil de ser feito escrevendo dois
loops distintos, um para contar valores crescentes e outro para valores decrescentes. Isso se d pelo fato de que,
quando estamos contando valores decrescentes, o operador que compara e verifica se o loop terminou precisa
ser
>=
ao invs de
<=
Pode ser til usar um valor de incremento diferente do valor padro (por exemplo -1) quando o valor final do
intervalo for menor do que o valor de incio. Dessa forma, ao invs de ficar preso em um loop infinito,
range(5, 2)
Invertendo um array
Os arrays possuem o mtodo
reverse
reverseArray
reverseArrayInPlace
. A primeira (
reverseArray
) recebe
um array como argumento e produz um novo array que tem os mesmos elementos com ordem inversa. A
segunda (
reverseArrayInPlace
reverse
os elementos do prprio array que foi fornecido como argumento. Ambas as funes no devem usar o mtodo
padro
reverse
Levando em considerao as notas sobre efeitos colaterais e funes puras do captulo anterior, qual verso voc
espera que seja til em mais situaes? Qual delas mais eficiente?
58
Dicas
Existem duas maneiras bvias de implementar
do incio ao fim e usar o mtodo
unshift
reverseArray
push
um pouco estranha (
).
reverseArrayInPlace
for
[Link](0)
reverseArray
O truque inverter o primeiro e o ltimo elemento, depois o segundo e o penltimo e assim por diante. Voc pode
fazer isso percorrendo at a metade do valor de
length
do array (use
[Link]
baixo, pois voc no precisa lidar com o elemento do meio de um array com tamanho mpar) e substituir o
elemento na posio
[Link] - 1 - i
armazenar temporariamente um dos elementos, sobrescrever o seu valor com o valor do elemento espelhado
(elemento que deseja substituir) e, por fim, colocar o valor da varivel local no lugar onde o elemento espelhado
estava originalmente.
A lista
Objetos tratados como agrupamentos genricos de valores, podem ser usados para construir diversos tipos de
estrutura de dados. Uma estrutura de dados comum a lista (no se confunda com o array). A lista um conjunto
de objetos, sendo que o primeiro objeto contm uma referncia para o segundo, o segundo para o terceiro, e
assim por diante.
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
Uma das vantagens das listas que elas podem compartilhar partes de sua estrutura. Por exemplo, se eu
criasse dois novos valores
(sendo que
list
uma referncia
varivel definida anteriormente), ambas sero listas independentes que compartilham a mesma estrutura que foi
59
usada para criar os trs ltimos elementos. Alm disso, a lista original ainda uma lista vlida com trs
elementos.
Escreva a funo
[1, 2, 3]
arrayToList
que constri uma estrutura de dados similar estrutura anterior quando fornecido
prepend
listToArray
por criar uma nova lista com esse novo elemento adicionado ao incio da lista original e, por fim, crie a funo
nth
que recebe uma lista e um nmero como argumentos e retorna o elemento que est na posio informada pelo
nmero ou
undefined
nth
Dicas
Construir uma lista mais fcil de ser feito de trs para frente. Portanto,
arrayToList
trs para frente (veja o exerccio anterior) e, para cada elemento, adicionar um objeto lista. Voc pode usar uma
varivel local para armazenar a parte da lista que foi criada e usar um padro parecido com
list}
listToArray
nth
), o seguinte loop
for
value
node
null
o loop finalizado.
A verso recursiva de
nth
ir, similarmente, olhar para uma parte ainda menor do tail (final) da lista e, ao mesmo
tempo, fazer a contagem regressiva do ndice at que chegue ao zero, significando que o ponto no qual ela pode
retornar a propriedade
value
do n que est sendo verificado. Para acessar o elemento na posio zero de uma
value
rest
da lista em questo.
Comparao "profunda"
O operador
==
compara objetos pelas suas identidades. Entretanto, algumas vezes, voc pode preferir comparar
deepEqual
true
forem objetos que possuem propriedades e valores iguais quando comparados usando uma chamada recursiva
de
deepEqual
Para saber se a comparao entre duas coisas deve ser feita pela identidade (use o operador
pela verificao de suas propriedades, voc pode usar o operador
typeof
. Se ele produzir
===
"object"
para isso) ou
para ambos
os valores, voc dever fazer uma comparao "profunda". Entretanto, voc deve levar em considerao uma
60
typeof null
tambm produz
"object"
Dicas
O teste para saber se est lidando com um objeto real dever ser parecido com
Tome cuidado para comparar as propriedades apenas quando ambos argumentos forem objetos. Em todos os
outros casos, voc pode simplesmente retornar imediatamente o resultado da aplicao do operador
Use um loop
for/in
===
para percorrer todas as propriedades. Voc precisa verificar se ambos os objetos possuem
o mesmo conjunto de propriedades e se essas propriedades tm valores idnticos. O primeiro teste pode ser
feito contando a quantidade de propriedades em cada objeto e retornar
false
mesmo, ento, percorra todas as propriedades de um objeto e, para cada uma delas, verifique se o outro objeto
tambm a possui. Os valores das propriedades so comparados usando uma chamada recursiva para
deepEqual
true
61
false
sum
range
do que o primeiro. Mesmo assim, eu diria que ele o mais provvel a estar correto.
A razo dele possivelmente ser o mais correto, que a soluo expressa em um vocabulrio que corresponde
ao problema que est sendo resolvido. Somar um intervalo de nmeros no se trata de laos de repetio e
contadores. Trata-se de intervalos e somas.
As definies desse vocabulrio (as funes
sum
range
contadores e outros detalhes secundrios. No entanto, devido ao fato de representarem conceitos mais simples,
elas acabam sendo mais fceis de se entender.
Abstrao
No contexto da programao esse tipo de vocabulrio geralmente expressado pelo termo abstraes.
Abstraes escondem detalhes e nos d a habilidade de falar sobre problemas em alto nvel (mais abstrato).
Isto uma analogia que compara duas receitas de sopa de ervilha:
62
"Coloque 1 copo de ervilha por pessoa num recipiente. Adicione gua at as ervilhas ficarem cobertas.
Deixa as ervilhas na gua por no mnimo 12 horas. Tire as ervilhas da gua e coloque-as numa panela.
Adicione 4 copos de gua por pessoa. Cubra a panela e deixe-as cozinhando por duas horas. Pegue meia
cebola por pessoa, corte em pedaos, adicione s ervilhas. Pegue um talo de aipo por pessoa, corte em
pedaos e adicione s ervilhas. Pegue uma cenoura por pessoa, corte em pedaos! Adicione s ervilhas.
Cozinhe por 10 minutos".
E a segunda receita:
"Para uma pessoa: 1 copo de ervilha, meia cebola, um talo de aipo e uma cenoura." Embeba as ervilhas por
12 horas, ferva por 2 horas em 4 copos de gua (por pessoa). Pique e adicione os vegetais. Deixe cozinhar
por mais 10 minutos".
A segunda bem menor e mais fcil de interpretar. Mas ela necessita de um conhecimento maior sobre algumas
palavras relacionadas cozinhar como: embeber, ferva, pique e vegetais.
Quando programamos no podemos contar com todas as palavras do dicionrio para expressar o que
precisamos. Assim cairemos no primeiro padro de receita - onde damos cada comando que o computador tem
que realizar, um por um, ocultando os conceitos de alto nveis que se expressam.
Perceber quando um conceito implora para ser abstrado em uma nova palavra um costume que tem de virar
algo natural quando programamos.
Abstraindo
Array
transversal
Funes, como vimos anteriormente, so boas maneiras para se criar abstraes. Mas algumas vezes elas ficam
aqum.
No captulo anterior, esse tipo de
loop
console
array
elemento atual. Deixando de lado a monstruosidade do cdigo, ele tambm nos d espao para possveis erros:
Podemos reusar a varivel
, digitar errado
length
como
lenght
, confundir as variveis
current
e por a
vai.
Ento vamos tentar abstrair isso em uma nova funo. Consegue pensar em alguma forma?
trivial escrever uma funo que passa sobre um
array
e chama
[Link]
function logEach(array) {
for (var i = 0; i < [Link]; i++)
[Link](array[i]);
}
Mas e se quisermos fazer algo diferente do que apenas registrar os elementos? Uma vez que "fazer alguma
coisa" pode ser representado com uma funo e as funes so apenas valores, podemos passar nossas aes
como um valor para a funo.
63
forEach
da funo.
var numbers = [1, 2, 3, 4, 5], sum = 0;
forEach(numbers, function(number) {
sum += number;
});
[Link](sum);
// 15
loop
clssico, com o seu corpo escrito como um bloco logo abaixo. No entanto o
corpo est dentro do valor da funo, bem como esta dentro dos parnteses da chamada de
forEach
. por isso
array
array
number
manualmente.
forEach
forEach
arrays
transversais.
function gatherCorrelations(journal) {
var phis = {};
for (var entry = 0; entry < [Link]; entry++) {
var events = journal[entry].events;
for (var i = 0; i < [Link]; i++) {
var event = events[i];
if (!(event in phis))
phis[event] = phi(tableFor(event, journal));
}
}
return phis;
}
Trabalhando com
forEach
function gatherCorrelations(journal) {
var phis = {};
[Link](function(entry) {
[Link](function(event) {
if (!(event in phis))
phis[event] = phi(tableFor(event, journal));
});
});
return phis;
}
64
Voc pode at escrever funes que fornecem novos tipos de fluxos de controles.
function unless(test, then) {
if (!test) then();
}
function repeat(times, body) {
for (var i = 0; i < times; i++) body(i);
}
repeat(3, function(n) {
unless(n % 2, function() {
[Link](n, "is even");
});
});
// 0 is even
// 2 is even
As regras de escopo lxico que discutimos no captulo 3 trabalham a nosso favor quando usamos funes dessa
maneira. No exemplo acima, a varivel
acessar as variveis que esto em torno delas. Eles podem desempenhar um papel similar aos blocos
usados em
loops
{}
e expresses condicionais. Uma diferena importante que variveis declaradas dentro das
funes internas no podem ser acessadas fora da funo. Isso geralmente algo bom.
Passando argumentos
65
A funo
declarada abaixo, envolve seu argumento em outra funo, isso gera uma grave deficincia.
noisy
function noisy(f) {
return function(arg) {
[Link]("calling with", arg);
var val = f(arg);
[Link]("called with", arg, "- got", val);
return val;
};
}
Se
receber mais de um parmetro, ele recebe apenas o primeiro. Poderamos acrescentar vrios argumentos
arg1
arg2
deixaria explcito quantos seriam suficientes. Essa soluo limita algumas informaes de
[Link]
array
como
objeto
apply
. Voc passa um
array
function transparentWrapping(f) {
return function() {
return [Link](null, arguments);
};
}
Essa funo intil, mas nos mostra o padro que estamos interessados, a funo passa todos os argumentos
dados para
para o objeto
. O primeiro argumento do
apply
, estamos passando
null
uma chamada de mtodo. Iremos voltar a ver isto novamente no prximo captulo.
JSON
Funes de ordem superior que aplicam uma funo para os elementos de um
JavaScript. O mtodo
arrays
forEach
array
so bastante usadas em
uma funo mais primitiva. Existe outras variantes disponveis como mtodos em
. Para acostumarmos com eles vamos brincar com um outro conjunto de dados.
H alguns anos, algum juntou um monte de arquivos e montou um livro sobre a histria do nome da minha
famlia (Haverbeke que significa Oatbrook). Eu abri na esperana de encontrar cavaleiros, piratas, e alquimistas...
mas o livro acaba por ser principalmente de agricultores de Flamengos. Para minha diverso extrai uma
informao sobre os meus antepassados e coloquei em um formato legvel por um computador.
O arquivo que eu criei se parece mais ou menos assim:
[
{"name": "Emma de Milliano", "sex": "f",
"born": 1876, "died": 1956,
"father": "Petrus de Milliano",
"mother": "Sophia van Damme"},
{"name": "Carolus Haverbeke", "sex": "m",
"born": 1832, "died": 1905,
"father": "Carel Haverbeke",
"mother": "Maria van Brussel"},
and so on
]
66
JSON
JSON
arrays
os nomes das propriedades devem ficar entre aspas duplas e apenas expresses de dados simples so
permitidos, no permitido chamadas de funes, variveis ou qualquer coisa que envolva clculo real.
Comentrios no so permitidos em
JSON
[Link]
[Link]
JSON
string
O varivel
est disponvel na
ANCESTRY_FILE
do meu arquivo
como uma
JSON
string
sandbox
Filtrando um array
Para encontrar as pessoas no conjunto de dados dos ancestrais que eram jovens em 1924, a seguinte funo
pode ser til. Ele filtra os elementos em uma matriz que no passa pelo teste.
function filter(array, test) {
var passed = [];
for (var i = 0; i < [Link]; i++) {
if (test(array[i]))
[Link](array[i]);
}
return passed;
}
[Link](filter(ancestry, function(person) {
return [Link] > 1900 && [Link] < 1925;
}));
// [{name: "Philibert Haverbeke", }, ]
array
test
test
retornado.
Trs pessoas no arquivo estavam vivas e jovens em 1924: meu av, minha av e minha tia-av.
Observe como a funo
filter
array
forEach
filter
um mtodo padro de
arrays
67
array
[Link]([Link](function(person) {
return [Link] == "Carel Haverbeke";
}));
// [{name: "Carolus Haverbeke", }]
array
map
transforma um
array
de ancestrais
array
array
array
array
array
"Maria Haverbeke"]
Curiosamente, as pessoas que viveram pelo menos 90 anos de idade so as mesmas trs que vimos antes, as
pessoas que eram jovens em 1920, passam a ser a gerao mais recente no meu conjunto de dados. Eu acho
que a medicina j percorreu um longo caminho.
Assim como
forEach
filter
map
arrays
arrays
exemplo atual, a soma do nosso intervalo de nmeros, um exemplo disso. Outro exemplo seria encontrar uma
pessoa com um ano de vida no conjunto de dados.
Uma operao de ordem superior que representa este padro chamada de reduce (diminui o tamanho do
array
). Voc pode pensar nisso como dobrar a matriz, um elemento por vez. Quando somado os nmeros, voc
inicia com o nmero zero e, para cada elemento, combina-o com a soma atual adicionando os dois.
Os parmetros para a funo
reduce
so, alm do
filter
map
array
68
array
padro do mtodo
reduce
array
contm apenas um elemento, voc no precisa enviar um valor inicial. O mtodo ir pegar o primeiro elemento do
array
Para usar o
reduce
e encontrar o meu mais antigo ancestral, podemos escrever algo parecido com isto:
[Link]([Link](function(min, cur) {
if ([Link] < [Link]) return cur;
else return min;
}));
// {name: "Pauwels van Haverbeke", born: 1535, }
Componibilidade
Considere como escreveramos o exemplo anterior (encontrar a pessoa mais velha) sem funes de ordem
superior. O cdigo no ficaria to ruim.
var min = ancestry[0];
for (var i = 1; i < [Link]; i++) {
var cur = ancestry[i];
if ([Link] < [Link])
min = cur;
}
[Link](min);
// {name: "Pauwels van Haverbeke", born: 1535, ...}
Existem mais variveis, e o programa est com duas linhas a mais, mesmo assim continuou bem fcil de
entender.
Funes de ordem superior so teis quando voc precisa compor funes. Como exemplo, vamos escrever um
cdigo que encontra a idade mdia para homens e mulheres no conjunto de dados.
function average(array) {
function plus(a, b) { return a + b; }
return [Link](plus) / [Link];
}
function age(p) { return [Link] - [Link]; }
function male(p) { return [Link] == "m"; }
function female(p) { return [Link] == "f"; }
[Link](average([Link](male).map(age)));
// 61.67
[Link](average([Link](female).map(age)));
// 54.56
plus
loop
como - determinando sexo, calculando a idade e a mdia dos nmeros. Podemos aplic-las uma de cada vez
para obtermos o resultado que estamos procurando.
Escrever um cdigo limpo fabuloso. Infelizmente essa clareza tem um custo.
O Custo
No mundo dos cdigos elegantes e lindos arco-ris, vive um monstro que estraga os prazeres chamado de
ineficincia.
Um programa que processa um
pode fazer algo com o
array
array
e produzir um novo
array
intermedirios
arrays
um pouco custoso.
Passar uma funo para
forEach
Mas chamadas de funes em JavaScript so custosas comparadas com os simples blocos de repetio.
E assim existem vrias tcnicas que ajudam a melhorar a clareza de um programa. Abstraes adiciona uma
camada a mais entre as coisas cruas que o computador faz e o conceito que estamos trabalhando, sendo assim
a mquina realiza mais trabalho. Esta no uma lei de ferro, existem linguagens de programao que tem um
suporte melhor para a construo de abstrao sem adio de ineficincias, at mesmo em JavaScript, um
programador experiente pode encontrar maneiras de escrever um cdigo abstrato e rpido. Mas um problema
que muito comum.
Existem vrias tcnicas que ajudam a esclarecer o cdigo. Elas adicionam camadas entre as coisas cruas que o
computador est fazendo com os conceitos que estamos trabalhando e faz com que a mquina trabalhe mais
rpido. Isso no uma lei inescapvel -- existem linguagens de programao que possuem um melhor suporte
para construir aplicaes sem adicionar ineficincias e, ainda em JavaScript, um programador experiente pode
encontrar jeitos de escrever cdigos relativamente abstratos que ainda so rpidos, porm um problema
frequente.
Felizmente muitos computadores so extremamente rpidos. Se voc estiver processando uma modesta coleo
de dados ou fazendo alguma coisa que tem de acontecer apenas em uma escala de tempo humano (digamos,
toda vez que o usurio clica em um boto), ento no importa se voc escreveu aquela soluo maravilhosa que
leva meio milissegundo ou uma super soluo otimizada que leva um dcimo de um milissegundo.
til saber quanto tempo mais ou menos leva um trecho de cdigo para executar. Se vocs tm um
de um
loop
(diretamente, ou atravs de um
o cdigo dentro do
repete e
loop
loop
loop
loop
que realize
loop
P
vezes, onde
M x N x P
loop
loop
loop
loop
dentro
interno),
de fora se
externo. Se esse
pode adicionar muitas operaes. Quando um programa lento o problema muitas das vezes pode estar
atribuda a apenas uma pequena parte do cdigo que fica dentro de um
loop
interno.
70
Agora o problema no totalmente simples como conseguir as propriedades do pai e ir contando quantos levam
at chegar a Pauwels. Existem vrios casos na rvore genealgica onde pessoas se casaram com seus primos
de segundo grau (pequenos vilarejos tm essas coisas). Isso faz com que as ramificaes da famlia se
reencontrem em certos lugares, o que significa que eu compartilho mais de 1/2G do meu genes com essa
pessoa, onde usaremos G como nmero de geraes entre Pauwels e mim. Esta frmula vem a partir da ideia
que de cada gerao divide o conjunto de genes em dois.
Uma maneira razovel de pensar sobre este problema olhar para ele como sendo um anlogo de
condensa um
array
reduce
, que
em um nico valor, por valores que combinam vrias vezes da esquerda para a direita. Neste
caso ns tambm queremos condensar a nossa estrutura de dados para um nico valor mas de uma forma que
segue as linhas da famlia. O formato dos dados a de uma rvore genealgica em vez de uma lista plana.
A maneira que ns queremos reduzir esta forma calculando um valor para uma determinada pessoa,
combinando com os valores de seus ancestrais. Isso pode ser feito de uma forma recursiva: se estamos
interessados em uma pessoa A, temos que calcular os valores para os pais de As, que por sua vez obriga-nos a
calcular o valor para os avs de As e assim por diante. A princpio isso iria exigir-nos a olhar para um nmero
infinito de pessoas, j que o nosso conjunto de dados finito, temos que parar em algum lugar. Vamos permitir
um valor padro para nossa funo de reduo, que ser utilizado para pessoas que no esto em nossos
dados. No nosso caso, esse valor simplesmente zero, pressupondo de que as pessoas que no esto na lista
no compartilham do mesmo DNA do ancestral que estamos olhando.
Dado uma pessoa, a funo combina os valores a partir de dois pais de uma determinada pessoa, e o valor
padro,
reduceAncestors
A funo interna (
valueFor
) lida com apenas uma pessoa. Atravs da magica da recursividade ela pode chamar a
si mesma para lidar com o pai e com a me. Os resultados junto com o objeto da pessoa em si, so passados
para
Podemos ento usar isso para calcular a quantidade de DNA que meu av compartilhou com Pauwels van
Haverbeke e depois dividir por quatro.
71
A pessoa com o nome Pauwels van Haverbeke obviamente compartilhada 100 por cento de seu DNA com
Pauwels van Haverbeke (no existem pessoas que compartilham o mesmo nome no conjunto de dados), ento a
funo retorna 1 para ele. Todas as outras pessoas compartilham a mdia do montante que os seus pais
compartilham.
Assim estatisticamente falando, eu compartilho cerca de 0,05 por cento do DNA de uma pessoa do sculo 16.
Deve-se notar que este s uma aproximao estatstica e, no uma quantidade exata. Este um nmero
bastante pequeno mas dada a quantidade de material gentico que carregamos (cerca de 3 bilhes de pares de
bases), provavelmente ainda h algum aspecto na mquina biolgica que se originou de Pauwels.
Ns tambm podemos calcular esse nmero sem depender de
reduceAncestors
geral (condensao de uma rvore genealgica) a partir do caso especfico (computao do DNA compartilhado)
podemos melhorar a clareza do cdigo permitindo reutilizar a parte abstrata do programa para outros casos. Por
exemplo, o cdigo a seguir encontra a porcentagem de antepassados conhecidos para uma determinada pessoa
que viveu mais de 70 anos (por linhagem, para que as pessoas possam ser contadas vrias vezes).
function countAncestors(person, test) {
function combine(current, fromMother, fromFather) {
var thisOneCounts = current != person && test(current);
return fromMother + fromFather + (thisOneCounts ? 1 : 0);
}
return reduceAncestors(person, combine, 0);
}
function longLivingPercentage(person) {
var all = countAncestors(person, function(person) {
return true;
});
var longLiving = countAncestors(person, function(person) {
return ([Link] - [Link]) >= 70;
});
return longLiving / all;
}
[Link](longLivingPercentage(byName["Emile Haverbeke"]));
// 0.129
Tais nmeros no so levados muito a srio, uma vez que o nosso conjunto de dados contm uma coleo
bastante arbitrria de pessoas. Mas o cdigo ilustra o fato de que
reduceAncestors
Binding
O mtodo
bind
, est presente em todas as funes, ele cria uma nova funo que chama a funo original mas
bind
string
. Ao chamar
filter
isInSet
cujos nomes esto em um conjunto especfico. Ns podemos escrever uma expresso de funo que faz a
72
chamada para
isInSet
isInSet
[Link]([Link]([Link](null, theSet)));
// same result
A chamada usando
bind
isInSet
com
theset
apply
null
Sumrio
A possibilidade de passar funes como argumento para outras funes no apenas um artifcio mas sim um
aspecto muito til em JavaScript. Ela nos permite escrever clculos com intervalos como funes, e chamar estas
funes para preencher estes intervalos, fornecendo os valores para funo que descrevem os clculos que
faltam.
Arrays
um
array
filter
array
reduce
forEach
map
array
em
um valor nico.
Funes tm o mtodo
apply
bind
array
que usado para criar uma verso parcial da funo que foi aplicada.
Exerccios
Juntando
Use o mtodo
reduce
array
concat
para juntar um
array
de
arrays
em um nico
array
73
average
Note que nem todas as mes mencionadas no conjunto de dados esto presentes no
array
. O objeto
byName
facilita a busca por um objeto pessoa atravs do nome. Esse mtodo pode ser til aqui.
function average(array) {
function plus(a, b) { return a + b; }
return [Link](plus) / [Link];
}
var byName = {};
[Link](function(person) {
byName[[Link]] = person;
});
// Your code here.
// 31.2
Dica:
Como nem todos os elementos do
array
de idade, a menos que saibamos a data de nascimento da me) teremos que aplicar de alguma maneira um filtro
antes de chamarmos o
average
. Voc pode fazer isso no primeiro passo, basta definir uma funo
map
e na funo de mapeamento
retornar a diferena de idade ou nulo se me for desconhecida. Em seguida voc pode chamar o
remover os elementos nulos antes de passar o
array
para o mtodo
average
hasKnownMother
filter
para
function average(array) {
function plus(a, b) { return a + b; }
return [Link](plus) / [Link];
}
// Your code here.
// 16: 43.5
//
17: 51.2
//
18: 52.8
//
19: 54.8
//
20: 84.7
//
21: 94
array
groupBy
arrays
array
e retorna um
e os membros do grupo.
Dica:
A essncia desse exemplo encontra-se no agrupamento dos elementos em um conjunto por alguns aspectos - a
divises do
array
de ancestrais em pequenos
arrays
74
Durante o processo de agrupamento, mantenha um objeto que associa os nomes dos sculos (nmeros) com
os
arrays
teremos que cri-los em tempo real. Depois de calcular o sculo para cada pessoa, vamos testar para saber se o
sculo j existe. Se no existir adicione um
array
array
loop
for/in
pode ser usado para escrever a mdia de idades para cada sculo individualmente.
Todos e alguns
tambm vm com os mtodos padres
Arrays
&&
true
predicada
retorna apenas
true
array
array
array
ou
false
. Assim como o
array
true
true
every
true
some
some
(todos) e
every
every
some
Dica:
As funes podem seguir um padro semelhante definio de
forEach
nica exceo que eles devem retornar imediatamente (com o valor direita) quando a funo predicada retorna
true
ou
false
array
75
return
aps o
loop
Histria
Essa histria, como a maioria das histrias de programao, comea com um problema de complexidade. A
teoria de que a complexidade pode ser administrada separando-a em pequenos compartimentos isolados um
do outro. Esses compartimentos acabaram ganhando o nome de ob jetos.
Um objeto um escudo duro que esconde a complexidade grudenta dentro dele e nos apresenta pequenos
conectores (como mtodos) que apresentam uma interface para utilizarmos o objeto. A ideia que a interface seja
relativamente simples e toda as coisas complexas que vo dentro do objeto possam ser ignoradas enquanto se
trabalha com ele.
76
Como exemplo, voc pode imaginar um objeto que disponibiliza uma interface para uma determinada rea na sua
tela. Ele disponibiliza uma maneira de desenhar formas ou textos nessa rea, mas esconde todos os detalhes de
como essas formas so convertidos para os pixels que compem a tela. Voc teria um conjunto de
mtodos-
desenharCirculo
, por exemplo- e essas sero as nicas coisas que voc precisa saber pra usar tal
objeto.
Essas ideias foram trabalhadas inicialmente por volta dos anos 70 e 80 e, nos anos 90, foram trazidas a tona por
uma enorme onda hype-a revoluo da programao orientada a objetos. De repente, existia uma enorme tribo de
pessoas declarando que objetos eram a maneira correta de programar-e que qualquer coisa que no envolvesse
objetos era uma loucura ultrapassada.
Esse tipo de fanatismo produz um monte de bobagem impraticvel, e desde ento uma espcie de contrarevoluo vem acontecendo. Em alguns crculos de desenvolvedores, os objetos tm uma pssima reputao
hoje em dia.
Eu prefiro olhar para esse problema de um ngulo prtico, e no ideolgico. Existem vrios conceitos teis, dentre
eles um dos mais importantes o encapsulamento (distinguir complexidade interna e interface externa), que a
cultura orientada a objetos tem popularizado. Vamos ver esses conceitos, pois eles valem a pena.
Esse captulo descreve uma pegada mais excntrica do JavaScript com foco nos objetos e na forma como eles se
relacionam com algumas tcnicas clssicas de orientao a objetos.
77
Mtodos
Mtodos so propriedades simples que comportam valores de funes. Isso um mtodo simples:
var coelho = {};
[Link] = function(linha) {
[Link]("O coelho diz '" + linha + "'");
};
[Link]("Estou vivo.");
// O coelho diz 'Estou vivo.'
Normalmente um mtodo precisa fazer alguma coisa com o objeto pelo qual ele foi chamado. Quando uma
funo chamada como um mtodo-visualizada como uma propriedade e imediatamente chamada, como em
[Link]()
-a varivel especial
this
no seu contedo vai apontar para o objeto pelo qual foi chamada.
function speak(line) {
[Link]("The " + [Link] + " rabbit says '" +
line + "'");
}
var whiteRabbit = {type: "white", speak: speak};
var fatRabbit = {type: "fat", speak: speak};
[Link]("Oh my ears and whiskers, " +
"how late it's getting!");
// The white rabbit says 'Oh my ears and whiskers, how
//
right now.'
apply
bind
this
para dar a sada do tipo de coelho que est falando. Lembrando que
apply
chamado
call
this
this
[Link](fatRabbit, ["Burp!"]);
// The fat rabbit says 'Burp!'
[Link]({type: "old"}, "Oh my.");
// The old rabbit says 'Oh my.'
Prototypes
Observe com ateno.
var empty = {};
[Link]([Link]);
// function toString(){}
[Link]([Link]());
// [object Object]
78
apply
bind
,o
call
S que no. Eu venho ocultando algumas informaes sobre como os objetos funcionam no JavaScript. Alm de
sua lista de propriedades, quase todos os objetos tambm possuem um prottipo, ou prototype. Um prototype
outro objeto que usado como fonte de fallb ack para as propriedades. Quando um objeto recebe uma chamada
em uma propriedade que ele no possui, seu prototype designado para aquela propriedade ser buscado, e
ento o prototype daquele prototype e assim por diante.
Ento quem o prototype de um objeto vazio? o ancestral de todos os prototypes, a entidade por trs de quase
todos os objetos,
[Link]
[Link]([Link]({}) ==
[Link]);
// true
[Link]([Link]([Link]));
// null
A funo
[Link]
As relaes dos objetos JavaScript formam uma estrutura em forma de rvore, e na raiz dessa estrutura se
encontra o
[Link]
. Ele fornece alguns mtodos que esto presentes em todos os objetos, como o
[Link]
[Link]
, e arrays derivam do
[Link]([Link](isNaN) ==
[Link]);
// true
[Link]([Link]([]) ==
[Link]);
// true
Por diversas vezes, o prototype de um objeto tambm ter um prototype, dessa forma ele ainda fornecer
indiretamente mtodos como
A funo
[Link]
toString
[Link]
para
Construtores
A maneira mais conveniente de criar objetos que herdam algum prototype compartilhado usar um construtor. No
JavaScript, chamar uma funo precedida pela palavra-chave
construtor. O construtor ter sua varivel
this
new
valor de outro objeto, esse novo objeto ser retornado a partir da chamada.
Um objeto criado com
new
Aqui est um construtor simples para coelhos. uma conveo iniciar o nome de um construtor com letra
maiscula para que seja fcil destingu-los das outras funes.
function Coelho(tipo) {
[Link] = tipo;
}
var coelhoAssassino = new Coelho("assassino");
var coelhoPreto = new Coelho("preto");
[Link]([Link]);
// preto
[Link]
Coelho
, que
esse objeto assim como seu prototype. Ento, para adicionar um mtodo
construtor
prototype
fala
[Link] = function(linha) {
[Link]("O coelho " + [Link] + " fala '" +
linha + "'");
};
[Link]("Doom...");
// O coelho preto fala 'Doom...'
importante notar a dinstino entre a maneira que um prototype associado a um construtor (por sua
propriedade prototype) e a maneira que objetos tm um prototype (que pode ser obtido com
[Link]
prototype
[Link]
, visto que os
height country
5895 Tanzania
Everest
8848 Nepal
Mount Fuji
3776 Japan
Mont Blanc
4808 Italy/France
Vaalserberg
323 Netherlands
Denali
Popocatepetl
5465 Mexico
A forma que nosso sistema de construir tabelas vai funcionar que a funo construtora vai perguntar para cada
clula quanto de altura e largura ela vai querer ter e ento usar essa informao para determinar a largura das
colunas e a altura das linhas. A funo construtora vai ento pedir para as clulas se desenharem no tamanho
correto e montar o resultado dentro de uma string.
O programa de layout vai comunicar com os objetos clulas atravs de uma interface bem definida. Dessa forma,
os tipos de clulas que o programa suporta no est definida antecipadamente. Ns podemos adicionar novas
clulas de estilo depois por exemplo, clulas sublinhadas para cabealho e se eles suportarem nossa
interface, isso vai simplesmente, funcionar, sem exigir alteraes no layout do programa.
80
Esta a interface:
minHeight()
minWidth()
retorna um nmero indicando a altura mnima que esta clula necessita (em linhas).
retorna um nmero indicando a largura mnima da clula (em caracteres).
draw(width, height)
height
width
underscore uma forma de indicar (para leitores humanos) que este argumento no ser usado.
A funo
rowHeights
map
map
reduce
(assim como
rows
filter
um segundo argumento funo fornecida: o ndice do elemento atual. Mapeando os elementos da primeira linha
e somente usando o segundo argumento da funo de mapeamento,
elemento para cada ndice da coluna. A chamada
reduce
81
colWidths
rows
function drawTable(rows) {
var heights = rowHeights(rows);
var widths = colWidths(rows);
function drawLine(blocks, lineNo) {
return [Link](function(block) {
return block[lineNo];
}).join(" ");
}
function drawRow(row, rowNum) {
var blocks = [Link](function(cell, colNum) {
return [Link](widths[colNum], heights[rowNum]);
});
return blocks[0].map(function(_, lineNo) {
return drawLine(blocks, lineNo);
}).join("\n");
}
return [Link](drawRow).join("\n");
}
A funo
drawTable
drawRow
drawRow
contedo das clulas, divididos por linha. Uma clula simples contendo apenas o nmero 3776 deve ser
representada por um array com um nico elemento como
["3776"]
["name", "----"]
Os blocos para uma linha, que devem todos ter a mesma largura, devem aparecer prximos um ao outro na sada
final. A segunda chamada a
map
em
drawRow
bloco mais esquerda e, para cada uma delas, coletando uma linha que expande a tabela para sua largura
mxima. Essas linhas so ento juntadas com caracteres newline para fornecer a linha completa e ser o valor
retornado de
A funo
drawRow
drawLine
extrai linhas que devem aparecer prximas uma a outra a partir de um array de blocos e as
junta com um caracter espao para criar o espao de um caracter entre as colunas da tabela.
Agora vamos escrever o construtor para clulas que contenham texto, implementando a interface para as clulas
da tabela. O construtor divide a linha em um array de linhas usando o mtodo string
split
minWidth
82
repetido
times
repeat
draw
"));
}
[Link](row);
}
[Link](drawTable(rows));
// ##
//
//
##
//
//
##
##
##
##
##
##
##
##
##
##
##
Funciona! Mas apesar de todas as clulas terem o mesmo tamanho, o cdigo do layout da tabela no faz nada
realmente interessante.
Os dados fonte para a tabela de montanhas que estamos tentando construir esto disponveis na varivel
MOUNTAINS
Vamos querer destacar a linha do topo, que contm o nome das colunas, sublinhando as clulas com uma srie
de caracteres trao. Sem problemas ns simplesmente escrevemos um tipo de clula que manipula o
sublinhado.
83
function UnderlinedCell(inner) {
[Link] = inner;
};
[Link] = function() {
return [Link]();
};
[Link] = function() {
return [Link]() + 1;
};
[Link] = function(width, height) {
return [Link](width, height - 1)
.concat([repeat("-", width)]);
};
Uma clula sublinhada contm outra clula. Ela reporta seu tamanho mnimo sendo o mesmo que da sua clula
interna (chamando os mtodos
minWidth
minHeight
height country
//
//
Kilimanjaro
//
etcetera
A funo padro
5895
Tanzania
[Link]
deve conter clulas sublinhadas que do os nomes das colunas. Abaixo disso, os valores de todos os objetos no
conjunto de dados aparecem como clulas normais - ns os extramos mapeando sobre o array
keys
de modo
que tenhamos certeza que a ordem das clulas a mesma em todas as linhas.
A tabela resultante se assemelha ao exemplo mostrado anteriormente, exceto que ela no alinha os nmeros
direita na coluna
height
minHeight
minWidth
construtor, o que adicionaria cdigo que no estritamente relevante para construo do objeto. Isso pode causar
problemas se, por exemplo, a clula interior de uma clula exterior mudou, onde nesse ponto o tamanho da clula
sublinhada tambm deve mudar.
84
Isso tem levado algumas pessoas a adotarem um princpio de nunca inclurem propriedades nonmethod em
interfaces. Ao invs de acessarem diretamente o valor da propriedade, eles usam mtodos
setSomething
getSomething
para ler e escrever propriedades. Esta abordagem tem a parte negativa de que voc ir acabar
get
ou
set
executada quando a propriedade for lida ou escrita. Voc pode tambm adicionar tal propriedade em um objeto
existente, por exemplo um prottipo, usando a funo
[Link]
set
, no objeto passado
defineProperty
setter. Quando um getter definido mas um setter no, escrever nessa propriedade algo simplesmente
ignorado.
Herana
Ns no estamos prontos com nosso exerccio de layout de tabela. Ela deve ajudar na leitura de nmeros
alinhados direita em colunas. Ns devemos criar outra tipo de clula como
TextCell
espao nas linhas do lado direito, vamos espa-las do lado esquerdo que ir alinhas direita.
Podemos simplesmente construir um novo construtor com todos os trs mtodos em seu prottipo. Mas
prottipos podem ter seus prprios prottipos, e isso nos permite fazer algo inteligente.
85
function RTextCell(text) {
[Link](this, text);
}
[Link] = [Link]([Link]);
[Link] = function(width, height) {
var result = [];
for (var i = 0; i < height; i++) {
var line = [Link][i] || "";
[Link](repeat(" ", width - [Link]) + line);
}
return result;
};
TextCell
minHeight
draw
minWidth
de
TextCell
. Um
RTextCell
agora basicamente
Este padro chamado herana. Isso nos permite construir tipos de dados levemente diferentes a partir de tipos
de dados existentes com relativamente pouco esforo. Tipicamente, o novo construtor vai chamar o antigo
construtor (usando o mtodo
call
para ser capaz de dar a ele o novo objeto assim como o seu valor
this
). Uma
vez que esse construtor tenha sido chamado, ns podemos assumir que todos os campos que o tipo do antigo
objeto supostamente contm foram adicionados. Ns organizamos para que o prottipo do construtor derive do
antigo prottipo, ento as instncias deste tipo tambm vo acesso s propriedades deste prottipo. Finalmente,
ns podemos sobrescrever algumas das propriedades adicionando-as ao nosso novo prottipo.
Agora, se ns ajustarmos sutilmente a funo
dataTable
para usar
RTextCell
86
Voc pode ter polimorfismo sem herana, como ns vimos. Eu no vou dizer para voc evitar herana
completamente. Eu a uso regularmente em meus programas. Mas voc deve v-la como um leve truque
desonesto que vai ajud-lo a definir novos tipos com menos cdigo, no como um grande princpio de
organizao de cdigo. Uma forma mais apropriada de extender tipos atravs da composio, como
UnderlinedCell
O operador
instanceof
Ocasionalmente til saber se um objeto foi derivado de um construtor em especfico. Para isso, o JavaScript
fornece um operador binrio chamado
instaceof
deriva de
[Link]
RTextCell
uma instncia de
TextCell
porque
Object
Resumo
Ento objetos so mais complicados do que inicialmente eu os retratei. Eles tem prottipos, que so outros
objetos, e vo agir como se tivessem propriedades que eles no tem caso seu prottipo tenha essa propriedade.
Objetos simples tem
[Link]
Construtores, que so funes cujos nomes usualmente iniciam com uma letra maiscula, podem ser usador
com o operador
prototype
new
para criar objetos. O prottipo do novo objeto ser o objeto encontrado na propriedade
da funo construtora. Voc pode fazer bom uso disso adicionando propriedades que todos os valores
instanceof
Exerccios
87
Um tipo de vetor
Escreva um construtor
parmetros
D ao prottipo de
Vector
dois mtodos,
plus
minus
this
eo
passado no parmetro).
Adicione uma propriedade getter
(
x, y
length
) at a origem (0,0).
Dicas
Sua soluo pode seguir o padro do construtor
Rabbit
Adicionar uma propriedade getter ao construtor pode ser feita com a funo
distncia do ponto
(0, 0)
at
(x, y)
[Link]
. Para calcular a
distncia que estamos procurando igual ao quadrado da coordenada x mais o quadrado da coordenada y.
Assim,
(x2 + y2)
[Link]
JavaScript.
Outra clula
Implemente uma clula do tipo
descrita anteriormente neste captulo. Ela deve envolver outra clula (como
clula resultante tem pelo menos a largura (
width
) e altura (
height
UnderlinedCell
naturalmente menor.
// Your code here.
var sc = new StretchCell(new TextCell("abc"), 1, 2);
[Link]([Link]());
// 3
[Link]([Link]());
// 2
[Link]([Link](3, 2));
// ["abc", "
"]
Dicas
Voc vai ter que armazenar os 3 argumentos construtores na instncia do objeto. Os mtodos
minHeight
draw
inner
minWidth
[Link]
).
Interface sequencial
88
Projete uma interface que abstraia interaes sobre uma coleo de valores. Um objeto que fornece esta interface
representa uma sequncia, e a interface deve de alguma forma tornar possvel para o cdigo que usa este objeto
iterar sobre uma sequncia, olhando para o valor dos elementos de que ela composta e tendo alguma forma de
saber quando o fim da sequncia foi atingido.
Quando voc tiver especificado sua interface, tente escrever uma funo
e chama
[Link]
logFive
para seus primeiros 5 elementos - ou menos, se a sequncia tiver menos do que cinco
elementos.
Ento implemente um tipo de objeto
ArraySeq
from
to
RangeSeq
em seu construtor).
Dicas
Uma forma de resolver isso fornecendo objetos sequenciais state, que significa que suas propriedades so
alteradas no seu processo de uso. Voc pode armazenar um contador que indica quo longe o objeto
sequenciais avanaram.
Sua interface vai precisar expor ao menos uma forma de pegar o prximo elemento e encontrar se a iterao j
chegou no fim da sequencia. tentador fazer isso em um mtodo,
next
, que retorna
null
ou
undefined
a sequncia chegar ao fim. Mas agora voc tem um problema quando a sequncia realmente tiver
null
quando
. Ento
um mtodo separado (ou uma propriedade getter) para descobrir se o fim foi alcanado provavelmente
prefervel.
Outra soluo evitar mudar o estado do objeto. Voc pode expor um mtodo para pegar o elemento atual (sem o
auxlio de nenhum contador) e outro para pegar uma nova sequncia que representa os elementos restantes
depois do atual (ou um valor especial se o fim da sequncia tiver sido atingido). Isso bem elegante - um valor
sequencial vai "permanecer ele mesmo" mesmo depois de ter sido usado e pode ser compartilhado com outro
cdigo sem a preocupao sobre o que pode acontecer com ele. Isso , infelizmente, algo um pouco ineficiente
numa linguagem como JavaScript porque envolve criar vrios objetos durante a iterao.
89
Definio
Para tornar esta tarefa gerencivel, vamos simplificar radicalmente o conceito de um mundo. Ou seja, um mundo
ser uma
grid
grid
Strings
grid
do mundo usando um
##",
"#
#",
"#
#####
"##
"###
"#
##
#",
##
#",
#",
###
"#
####
"#
##
"# o
"#
#",
#",
o
#",
o
### #",
#",
"############################"];
Os caracteres
"#"
"O"
representam os bichos. Os
toString
impresso (similar ao plano que foi baseado) para que possamos ver o que est acontecendo l dentro. O objeto
do mundo tambm tem um mtodo por sua vez que permite que todos os bichos podem darem uma volta e
atualizar o mundo para terem suas aes.
Representando o espao
90
grid
modela o mundo com uma largura e altura fixa. Os quadrados so identificados pelas suas coordenadas
x e y. Ns usamos um tipo simples, Vector(como visto nos exerccios do captulo anterior) para representar esses
pares de coordenadas.
function Vector(x, y) {
this.x = x;
this.y = y;
}
[Link] = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
grid
.A
grid
objeto separado(que ser uma propriedade de um objeto do mundo) para manter o objeto bem simples. O
mundo deve preocupar-se com as coisas relacionadas com o mundo e a
relacionadas da
grid
grid
grid
array
de
arrays
tendo duas
"top middle",
"top right"],
Ou podemos usar uma nica matriz com largura x altura e decidir que o elemento
x + (y * largura)
(x, y)
encontrado na posio
na matriz.
"top middle",
"top right",
Uma vez que o acesso real a essa matriz esta envolto em mtodos de tipo do objeto da
grid
, no importa o
cdigo que adotamos para abordagem. Eu escolhi a segunda representao pois torna muito mais fcil para criar
a matriz. Ao chamar o construtor de
Array
com um nico argumento, ele cria uma nova matriz vazia com o
grid
91
"move"
A ao pode tambm conter informao extra de alguma direo que o bicho possa se mover.
Bichos so terrivelmente mopes e podem ver apenas os quadrados em torno da
pode ser til ao decidir que ao tomar. Quando o mtodo
act
grid
bicho inspecione seus arredores. Ns vamos nomear oito quadrados vizinhos para ser as coordenadas:
para norte,
"ne"
"n"
para nordeste e assim por diante. Aqui est o objeto, vamos utilizar para mapear os nomes das
offsets
var directions = {
"n":
new Vector( 1,
0),
1),
"s":
new Vector( 0,
1),
1),
"w":
0),
new Vector(-1,
O objeto de exibio tem um mtodo que observa em qual direo o bicho esta indo e retorna um personagem
por exemplo, um "#" quando h uma parede na direo ou um "" (espao) quando no h nada. O objeto tambm
fornece os mtodos
find
findAll
a direo em que o personagem pode ser encontrado ao lado do bicho ou retorna nulo se no existir nenhum
sentido. O segundo retorna um
array
uma criatura sentada esquerda(oeste) de um muro vai ter ["ne", "e", "se"] ao chamar
findAll
passando o
92
function randomElement(array) {
return array[[Link]([Link]() * [Link])];
}
var directionNames = "n ne e se s sw w nw".split(" ");
function BouncingCritter() {
[Link] = randomElement(directionNames);
};
[Link] = function(view) {
if ([Link]([Link]) != " ")
[Link] = [Link](" ") || "s";
return {type: "move", direction: [Link]};
};
A funo auxiliar
randomElement
[Link]
para obter um ndice aleatrio. Vamos usar isso de novo mais tarde porque a aleatoriedade pode ser til em
simulaes.
Para escolher uma direo aleatria o construtor de
BouncingCritter
chama
[Link]
randomElement
em uma matriz de
definimos anteriormente, mas no garantido a ordem em que as propriedades sero listadas. Na maioria das
situaes os motores modernos de JavaScript retornam as propriedades na ordem em que foram definidos, mas
eles no so obrigados a terem tais comportamentos.
O
|| "s"
[Link]
est preso em um espao vazio em torno dele(por exemplo, quando um canto esta lotado de outros bichos).
O objeto do mundo
Agora podemos comear a fazer o objeto mundo. O construtor tem um plano(a matriz de
grid
Strings
que representa a
do mundo como descrito anteriormente) e uma legenda como argumentos. A legenda um objeto que nos
diz o que cada personagem no mapa significa. Ela contm um construtor para cada personagem, exceto para o
caractere de espao que sempre se refere como
null
espao vazio.
function elementFromChar(legend, ch) {
if (ch == " ")
return null;
var element = new legend[ch]();
[Link] = ch;
return element;
}
function World(map, legend) {
var grid = new Grid(map[0].length, [Link]);
[Link] = grid;
[Link] = legend;
[Link](function(line, y) {
for (var x = 0; x < [Link]; x++)
[Link](new Vector(x, y),
elementFromChar(legend, line[x]));
});
}
93
Em
primeiro criamos uma instncia do tipo correto, observando o construtor do carter aplicando
elementFromChar
originChar
originChar
toString
constri uma sequncia de mapeamento de estado atual do mundo atravs da realizao de um ciclo de duas
dimenses sobre os quadrados na
grid
function charFromElement(element) {
if (element == null)
return " ";
else
return [Link];
}
[Link] = function() {
var output = "";
for (var y = 0; y < [Link]; y++) {
for (var x = 0; x < [Link]; x++) {
var element = [Link](new Vector(x, y));
output += charFromElement(element);
}
output += "\n";
}
return output;
};
A parede um objeto simples que usado apenas para ocupar espao e no tem nenhum mtodo de ao.
function Wall() {}
Vamos criar um objeto Mundo com base no plano passado no incio do captulo, em seguida iremos chamar
toString
sobre ele.
//
//
#####
//
##
//
###
//
//
####
//
##
//
# o
//
//
############################
This
##
#
#
##
##
###
#
o
#
o
### #
#
e seu escopo
forEach
forEach
recebe o seu prprio escopo de modo que o escopo presente na funo interna no se refere ao objeto externo
recm-construdo. Na verdade quando a funo no chamada como um mtodo isso refere ao objeto global.
Isso significa que no podemos escrever
uma varivel local na funo exterior da
[Link]
grid
loop
. Podemos criar
Isso um erro de
design
este problema. Enquanto isso existem solues alternativas. Um padro comum dizer
uma
bind
var test = {
prop: 10,
addPropTo: function(array) {
return [Link](function(elt) {
return [Link] + elt;
}.bind(this));
}
};
[Link]([Link]([5]));
// [15]
A funo mapeia um
array
valor de um elemento do
e retorna o valor do
array
prop
test
somado ao resultado do
forEach
map
que pode ser usado para fornecer um escopo para dentro do bloco de interao (segundo argumento do
interador). Assim, voc poder expressar o exemplo anterior de uma forma um pouco mais simples.
var test = {
prop: 10,
addPropTo: function(array) {
return [Link](function(elt) {
return [Link] + elt;
}, this); // no bind
}
};
[Link]([Link]([5]));
// [15]
Isso funciona apenas para as funes de interaes que suportam tal parmetro de contexto. Quando algum
mtodo no suporta receber um contexto voc ir precisar usar as outras abordagens.
Em nossas prprias funes de interaes podemos apoiar tal parmetro de contexto enviando um segundo
argumento no bloco. Por exemplo, aqui no mtodo
determinada funo para cada elemento da
grid
forEach
grid
, chamaremos uma
Dando vida
O prximo passo escrever um mtodo para o objeto mundo que d aos bichos a chance de movimento. Ele vai
passar por cima da
mtodo
act
grid
usando o mtodo
forEach
. Quando ele encontra um ele chama o mtodo para obter uma ao e realiza a ao quando ela for
"move"
sero compreendidas.
95
Existe um problema com esta abordagem. Voc consegue identificar? Se deixarmos as criaturas se mover
livremente, eles podem se mover para um quadrado que no existe, e ns vamos permitir que eles se mova
novamente quando estiver dentro do quadrado vazio. Assim temos que ficar mantendo uma variedade de criaturas
que j sumiram ao invs de apenas ignorarmos.
[Link] = function() {
var acted = [];
[Link](function(critter, vector) {
if ([Link] && [Link](critter) == -1) {
[Link](critter);
[Link](critter, vector);
}
}, this);
};
forEach
grid
para
os bichos se movam.
[Link] = function(critter, vector) {
var action = [Link](new View(this, vector));
if (action && [Link] == "move") {
var dest = [Link](action, vector);
if (dest && [Link](dest) == null) {
[Link](vector, null);
[Link](dest, critter);
}
}
};
[Link] = function(action, vector) {
if ([Link]([Link])) {
var dest = [Link](directions[[Link]]);
if ([Link](dest))
return dest;
}
};
Em primeiro lugar, ns simplesmente pedimos para o bicho se mover, passando um objeto de exibio que tem
informaes sobre o mundo e a posio atual do bicho naquele mundo(vamos definir a tela em algum momento).
O mtodo retorna alguma tipo de ao.
Se o tipo de ao no um
"move"
"move"
bicho na prximo
Perceba que
quadrado
letAct
null
propriedade do tipo faz sentido. Este tipo de programao defensiva faz sentido em algumas situaes. A principal
razo para faz-la validar alguma fonte proveniente que no seja de controle(como alguma entrada de valores
definidas por usurios), mas tambm pode ser til para isolar outros subsistemas. Neste caso a inteno que
os bichos podem serem programados de forma no cuidadosa, eles no tm de verificar se suas aes de
destinado faz sentido. Eles podem simplesmente solicitar uma ao e o mundo que vai permitir a ao.
Estes dois mtodos no fazem a parte da interface externa de um objeto do mundo. Eles so um detalhe interno.
Algumas lnguas fornece maneiras de declarar explicitamente certos mtodos e propriedades privadas e sinalizar
um erro quando voc tenta us-los de fora do objeto. JavaScript no faz isso ento voc vai ter que confiar em
alguma outra forma de comunicao para descrever o que faz ou no parte da interface de um objeto. s vezes ele
96
pode ajudar a usar um esquema de nomes para distinguir entre as propriedades externas e internas, por
exemplo, prefixando todas as propriedades internas com um caractere sublinhado(
usos acidentais de propriedades que no fazem parte da interface de um objeto fique mais fcil de detectar.
A nica parte que falta para a tela se parece com isso:
function View(world, vector) {
[Link] = world;
[Link] = vector;
}
[Link] = function(dir) {
var target = [Link](directions[dir]);
if ([Link](target))
return charFromElement([Link](target));
else
return "#";
};
[Link] = function(ch) {
var found = [];
for (var dir in directions)
if ([Link](dir) == ch)
[Link](dir);
return found;
};
[Link] = function(ch) {
var found = [Link](ch);
if ([Link] == 0) return null;
return randomElement(found);
};
grid
grid
e se o
uma paredes no modo que podemos definir um mundo que no murado mas os bichos no podero caminhar
fora das bordas.
O movimento
Ns instanciamos o objeto mundo antes. Agora que ns adicionamos todos os mtodos necessrios, devemos
fazer os movimentos dos elementos no mundo.
for (var i = 0; i < 5; i++) {
[Link]();
[Link]([Link]());
}
// five turns of moving critters
Imprimir vrias cpias do mundo uma forma bastante desagradvel para movimentar um mundo. por isso que
o
sandbox
animateWorld
que executa uma animao, movendo o mundo com trs voltas por
stop
animateWorld(world);
// life!
A implementao do
animateWorld
parece algo misterioso agora, mas depois que voc ler os captulos deste livro
O destaque dramtico do nosso mundo quando duas criaturas saltam para fora. Voc consegue pensar em
outra forma interessante de comportamento?
O bicho que se move ao longo das paredes. Conceitualmente o bicho mantm a sua mo esquerda(pata,
tentculo ou o que for) para a parede e segue junto a ela. Este jeito acaba sendo no muito trivial de implementar.
Precisamos ser capazes de calcular as direes com a bssola. As direes so modelados por um conjunto de
String
dirPlus
significa 45 graus no sentido horrio para norte quando retornar "ne". Da mesma forma
dirPlus("n", 1)
dirPlus("s", -2)
function dirPlus(dir, n) {
var index = [Link](dir);
return directionNames[(index + n + 8) % 8];
}
function WallFollower() {
[Link] = "s";
}
[Link] = function(view) {
var start = [Link];
if ([Link](dirPlus([Link], -3)) != " ")
start = [Link] = dirPlus([Link], -2);
while ([Link]([Link]) != " ") {
[Link] = dirPlus([Link], 1);
if ([Link] == start) break;
}
return {type: "move", direction: [Link]};
};
O mtodo
act
"varre"
if
bicho acaba de passar algum tipo de obstculo, no caso, se o espao atrs e esquerda do bicho no estiver
vazio. Caso contrrio, o bicho comea a digitalizar diretamente frente de modo que ele vai andar em linha reta
ate um espao vazio.
E finalmente h um teste comparando
[Link]
que o circuito no vai correr para sempre quando o bicho est no muro ou quando o mundo esta lotados de outros
bichos no podendo achar quadrados vazios.
Este pequeno mundo demonstra as criaturas na parede:
animateWorld(new World(
["############",
"#
#",
"#
~ #",
"#
##
#",
"#
##
o####",
"#
#",
"############"],
{"#": Wall,
"~": WallFollower,
"o": BouncingCritter}
));
98
letAct
. Poderamos
simplesmente substituir o prottipo global do mtodo mas eu gostei muito da nossa simulao e gostaria que os
novos bichos mantivesse o mesmo jeito do velho mundo.
Uma soluo usar herana. Criamos um novo construtor,
prottipo global, mas que substitui o mtodo
letAct
LifelikeWorld
. O novo mtodo
letAct
actionTypes
O novo mtodo
letAct
verifica primeiro se uma ao foi devolvido, ento se a funo manipuladora para este tipo
true
que usamos uma chamada para dar o acesso ao manipulador do mundo, atravs de sua chamada. Observe que
para dar o acesso ao manipulador no mundo, precisamos fazer uma chamada.
Se a ao no funcionou por algum motivo a ao padro que a criatura simplesmente espere. Perde um quinto
de sua energia e se o seu nvel de energia chega a zero ou abaixo a criatura morre e removido da
grid
Manipuladores de aes
A ao mais simples que uma criatura pode executar
de ao como
{type: "grow"}
"crescer"
99
[Link] = function(critter) {
[Link] += 0.5;
return true;
};
Crescer com sucesso acrescenta meio ponto no nvel total da reserva de energia.
Analise o mtodo para se mover
[Link] = function(critter, vector, action) {
var dest = [Link](action, vector);
if (dest == null ||
[Link] <= 1 ||
[Link](dest) != null)
return false;
[Link] -= 1;
[Link](vector, null);
[Link](dest, critter);
return true;
};
checkDestination
. Se no vlido, se o destino
false
ao foi feita. Caso contrrio ele move o bicho e subtrai sua energia.
Alm de movimentar, os bichos pode comer.
[Link] = function(critter, vector, action) {
var dest = [Link](action, vector);
var atDest = dest != null && [Link](dest);
if (!atDest || [Link] == null)
return false;
[Link] += [Link];
[Link](dest, null);
return true;
};
Comer um outro bicho tambm envolve o fornecimento de um quadrado de destino vlido. Desta vez o destino no
pode estar vazio e deve conter algo com energia, por exemplo um bicho(mas no pode ser a parede pois elas no
so comestveis). Sendo assim a energia a partir da comida transferido para o comedor e a vtima retirada da
grid
Reproduzir custa duas vezes mais o nvel de energia de um bicho recm-nascido. Ento primeiro criamos o beb
(hipoteticamente) usando
elementFromChar
podemos encontrar o seu nvel de energia e testar se o pai tem energia suficiente para traz-lo com sucesso no
100
grid
pai.
As plantas comeam com um nvel de energia randomizados entre 3 e 7, isso para que eles no se reproduzam
todos no mesmo tempo. Quando a planta atinge nvel 15 de energia e no h espao vazio nas proximidades ela
no se reproduz. Se uma planta no pode