0% acharam este documento útil (0 voto)
6K visualizações303 páginas

JavaScript Eloquente

O documento descreve os principais tipos de valores e operadores em JavaScript, incluindo números, strings e Booleanos. Ele explica como números são armazenados em bits e as limitações de precisão, e introduz as operações aritméticas básicas como adição, subtração, multiplicação e divisão.

Enviado por

Ptter Venturin
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
6K visualizações303 páginas

JavaScript Eloquente

O documento descreve os principais tipos de valores e operadores em JavaScript, incluindo números, strings e Booleanos. Ele explica como números são armazenados em bits e as limitações de precisão, e introduz as operações aritméticas básicas como adição, subtração, multiplicação e divisão.

Enviado por

Ptter Venturin
Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Table of Contents

Introduction

1.1

Valores, Tipos e Operadores

1.2

Estrutura do Programa

1.3

Funes

1.4

Estrutura de Dados: Objeto e Array

1.5

Funes de Ordem Superior

1.6

A Vida Secreta dos Objetos

1.7

Prtica: Vida Eletrnica

1.8

Erros e Manipulao de Erros

1.9

Expresses Regulares

1.10

Mdulos

1.11

Prtica: A Linguagem de Programao

1.12

JavaScript e o Navegador

1.13

O Document Object Model

1.14

Manipulando Eventos

1.15

Projeto: Plataforma de Jogo

1.16

Desenhando no Canvas

1.17

HTTP

1.18

Formulrios e Campos de Formulrios

1.19

Projeto: Um Programa de Pintura

1.20

[Link]

1.21

Projeto: Website de Compartilhamento de Habilidades

1.22

JavaScript Eloquente - 2 edio


Uma moderna introduo ao JavaScript, programao e maravilhas digitais.
Mantenedor: Eric Douglas

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

11. Prtica: A Linguagem de Programao


12. JavaScript e o Navegador - (Parte 2: Navegador)
13. O Document Ob ject Model
14. Manipulando Eventos
15. Projeto: Plataforma de Jogo
16. Desenhando no Canvas
17. HTTP
18. Formulrios e Campos de Formulrios
19. Projeto: Um Programa de Pintura
20. [Link] - (Parte 3: [Link])
21. Projeto: Website de Compartilhamento de Habilidades

Status Geral do Projeto


As informaes sobre o status e log de cada captulo esto organizadas nessa issue.
Atualmente, estamos melhorando o que j est traduzido, focando na qualidade e preciso da traduo e
entendimento do texto como um todo, alm de tentar aplicar a gramtica mais correta possvel. Vrios
contribuidores ajudaram em diferentes partes do livro e, por isso, existem diversas oportunidades de melhorias.

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.

Valores, Tipos e Operadores


Abaixo da parte superficial da mquina, o programa se movimenta. Sem esforo, ele se expande e se
contrai. Com grande harmonia, os eltrons se espalham e se reagrupam. As formas no monitor so como
ondulaes na gua. A essncia permanece invisvel por baixo.
Master Yuan-Ma, The Book of Programming
Dentro do mundo do computador, h somente dados. Voc pode ler, modificar e criar novos dados, entretanto,
qualquer coisa que no seja um dado simplesmente no existe. Todos os dados so armazenados em longas
sequncias de bits e so, fundamentalmente, parecidos.
Bits podem ser qualquer tipo de coisa representada por dois valores, normalmente descritos como zeros e uns.
Dentro do computador, eles representam formas tais como uma carga eltrica alta ou baixa, um sinal forte ou
fraco ou at um ponto na superfcie de um CD que possui brilho ou no. Qualquer pedao de informao pode ser
reduzido a uma sequncia de zeros e uns e, ento, representados por bits.
Como um exemplo, pense sobre a maneira que o nmero 13 pode ser armazenado em bits. A forma usual de se
fazer esta analogia a forma de escrevermos nmeros decimais, mas ao invs de 10 dgitos, temos apenas 2. E,
ao invs de o valor de um dgito aumentar dez vezes sobre o dgito aps ele, o valor aumenta por um fator de 2.
Estes so os bits que compem o nmero treze, com o valor dos dgitos mostrados abaixo deles:
0

128

64

32

16

Assim, este o nmero binrio 00001101, ou 8 + 4 + 1, que equivale a 13.

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

Isso 2.998 x 10 = 299800000.


Clculos usando nmeros inteiros menores que os 9 quadrilhes mencionados, sero com certeza precisos.
Infelizmente, clculos com nmero fracionrios normalmente no so precisos. Da mesma forma que (pi) no
pode ser expresso de forma precisa por uma quantidade finita de dgitos decimais, muitos nmeros perdem sua
5

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

so chamados de operadores. O primeiro referente adio e o segundo multiplicao.

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

Para subtrao existe o operador

e para a diviso usamos o operador

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

e, de forma similar, os operadores

possui a

possuem a mesma precedncia entre si.

Quando vrios operadores de mesma precedncia aparecem prximos uns aos outros, como por exemplo
+ 1

, eles so aplicados da esquerda para a direita:

(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

. A precedncia do operador resto a mesma da multiplicao e diviso. Voc ouvir com

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

, que so usados para representar os infinitos positivo e negativo. O

Infinity

, assim como qualquer outra variao dessa conta. Entretanto, no

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

(zero dividido por zero),

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

significar um caractere de tabulao. Considere a seguinte string:

"This is the first line\nAnd this is the second"

O texto na verdade ser:


This is the first line
And this is the second

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.

pode ser representada:

"A newline character is written like \"\\n\"."

Strings no podem ser divididas, multiplicadas nem subtradas, entretanto, o operador

pode ser usado nelas.

Ele no efetua a adio, mas concatena, ou seja, junta duas strings em uma nica string. O prximo exemplo
produzir a string

"concatenate"

"con" + "cat" + "e" + "nate"

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

espera um nico valor. Operadores que

usam dois valores so chamados de operadores b inrios, enquanto que aqueles que recebem apenas um, so
chamados de operadores unrios. O operador

pode ser usado tanto como binrio quanto como unrio.

[Link](- (10 - 2))


// -8

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

>

<

so tradicionalmente smbolos para representar maior que e menor que

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

verdadeiro. Alm disso, caracteres no alfabticos (!, -, e

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

>=

(maior que ou igual a),

igual a).
[Link]("Itchy" != "Scratchy")
// true

<=

(menor que ou igual a),

==

(igual a) e

!=

(no

Existe apenas um valor no JavaScript que no igual a ele mesmo, que o valor

NaN

. Ele significa not a

numb er, que em portugus seria traduzido como no um nmero.


[Link](NaN == NaN)
// false

NaN

supostamente usado para indicar o resultado de alguma operao que no tenha sentido e, por isso, ele

no ser igual ao resultado de quaisquer outras operaes sem sentido.

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

apenas verdadeiro se ambos os valores dados ele forem verdadeiros.


[Link](true && false)
// false
[Link](true && true)
// true

O operador

||

indica o valor lgico or ou, em portugus, ou. Ele produz um valor verdadeiro se qualquer um dos

valores dados ele for verdadeiro.


[Link](false || true)
// true
[Link](false || false)
// false

Not, em portugus no, escrito usando um ponto de exclamao (


valor que dado ele. Por exemplo,

!true

produz

false

). Ele um operador unrio que inverte o

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

>

&&

, em seguida vm os operadores de comparao (

possui a menor precedncia, depois vem o


==

, etc) e, por ltimo, quaisquer outros

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

, que so usados para indicar a ausncia de um valor com

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

simplesmente porque eles precisam retornar algum valor.

A diferena de significado entre

undefined

null

um acidente que foi criado no design do JavaScript, e no faz

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).

Converso Automtica de Tipo


Na introduo, mencionei que o JavaScript tentar fazer o seu melhor para aceitar quase todos os programas que
voc fornecer, inclusive aqueles que fazem coisas bem estranhas. Isso pode ser demonstrado com as seguintes
expresses:
[Link](8 * null)
// 0
[Link]("5" - 1)
// 4
[Link]("5" + 1)
// 51
[Link]("five" * 2)
// NaN
[Link](false == 0)
// true

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

produzido. Quaisquer outras operaes aritmticas realizadas com

, portanto, quando voc perceber que est recebendo esse valor em algum lugar

inesperado, procure por converses acidentais de tipo.


Quando comparamos valores do mesmo tipo usando o operador

==

, o resultado fcil de se prever: voc

receber verdadeiro quando ambos os valores forem o mesmo, exceto no caso de

NaN

. Por outro lado, quando

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

apenas se ambos os lados forem

undefined
null

ou

aparece em algum dos lados do operador, ser produzido verdadeiro


undefined

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

, voc pode simplesmente compar-lo a

null

com o operador

==

).

Mas e se voc quiser testar se algo se refere ao valor preciso


nmeros para valores booleanos afirmam que
outros valores contam como

true

false

? As regras de converso de strings e

e empty strings contam como

NaN

. Por causa disso, expresses como

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:

===

!==

. O primeiro teste se o valor precisamente igual ao outro, e o segundo testa se

ele no precisamente igual. Ento

falso como esperado.

"" === false

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

, no h problemas em usar os operadores

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

resultado vai ser verdadeiro, e


X

, no importa o que

- pode ser uma expresso que faa algo terrvel - o

nunca avaliado. O mesmo acontece para

false && X

, que falso, e vai ignorar

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"

). Eles podem ser

combinados e transformados com operadores. Ns vimos operadores binrios para aritmtica (


%

), um para concatenao de string (

), comparao (

==

11

!=

===

!==

<

>

<=

>=

&&

||

) e lgica (

,e

),

como tambm vrios operadores unrios (

para negativar um nmero,

para negar uma lgica, e

typeof

para encontrar o tipo do valor).


Isto lhe d informao suficiente para usar o JavaScript como uma calculadora de bolso, mas no muito mais. O
prximo captulo vai comear a amarrar essas operaes bsicas conjuntamente dentro de programas bsicos.

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

operador binrio aplicado a duas expresses, ou um unrio aplicado a uma.


Isso mostra parte da beleza da interface baseada na linguagem. Expresses podem ser encadeadas de forma
semelhante s subfrases usadas na linguagem humana - uma subfrase pode conter sua prpria subfrase, e
assim por diante. Isto nos permite combinar expresses para expressar computaes complexas arbitrariamente.
Se uma expresso corresponde a um fragmento de sentena, uma afirmao, no JavaScript, corresponde a uma
frase completa em linguagem humana. Um programa simplesmente uma lista de afirmaes.
O tipo mais simples de afirmao uma expresso com um ponto e vrgula depois dela. Este o programa:
1;
!false;

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;

E isso nos d um segundo tipo de declarao. A palavra especial (palavra-chave)

var

indica que esta sentena

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.

A declarao anterior criou uma varivel chamada

caught

e a usou para armazenar o valor que foi produzido pela

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

apont-las para um novo:


var mood = "light";
[Link](mood);
// light
mood = "dark";
[Link](mood);
// dark

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 luigisDebt = 140;


luigisDebt = luigisDebt - 35;
[Link](luigisDebt);
// 105

Palavras-chave e Palavras Reservadas


Palavras que tem um significado especial, como

var

, no podem ser usadas como nomes de variveis. Estas

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

Muitos dos valores fornecidos no ambiente padro so do tipo

function

(funo). Uma funo um pedao de

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

detm uma funo que mostra uma pequena caixa de

dilogo com uma mensagem. usada da seguinte forma:


alert("Good morning!");

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

que foi passada como o texto a ser

mostrado na caixa de dilogo. Os valores passados para funes so chamados de


funo

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]

para sada de valores. A

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

console JavaScript. Esta parte da interface do


quando voc pressiona

F12

browser

fica oculta por padro, mas muitos browsers abrem

, ou no Mac, quando voc pressiona

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]

vai mostrar embaixo

o exemplo, ao invs de ser no console JavaScript.


var x = 30;
[Link]("o valor de x ", x);
// o valor de x 30

Embora eu tenha afirmado que nomes de variveis no podem conter pontos,

[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

do valor contido na varivel

console

. Ns vamos entender o que isso significa no captulo 4.

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]

, que pega dois nmeros e retorna o maior

Quando uma funo produz um valor, dito que ela retorna (

return

) ele. Em JavaScript, tudo que produz um valor

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]

, usada como uma das

entradas para o operador de soma:


[Link]([Link](2, 4) + 100);

O prximo captulo explica como ns podemos escrever nossas prprias funes.

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

. Isto retorna um valor booleano:

true

se o

se o usurio clica em Cancel.

confirm("Shall we, then?");

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

converte o valor para um nmero. Ns precisamos dessa converso pois o resultado de

um valor do tipo

string

, e ns queremos um nmero. Existem funes similares chamadas

que convertem valores para estes tipos.

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:

A execuo condicional escrita, em JavaScript, com a palavra-chave

if

. No caso mais simples, ns queremos

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

executa ou no uma declarao baseada no resultado de uma expresso Booleana. Tal

expresso escrita entre parnteses logo aps a palavra-chave e seguida por uma declarao a ser executada.
A funo
funo

isNaN

Number

uma funo padro do JavaScript que retorna


retorna

NaN

true

apenas se o argumento fornecido for

NaN

a condio se traduz a "a no ser que

theNumber

no seja um nmero, faa isso".

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,

pode ser usada, juntamente com

if

, para criar dois caminhos distintos de execuo.

var theNumber = Number(prompt("Pick a number", ""));


if (!isNaN(theNumber))
alert("Your number is the square root of " +
theNumber * theNumber);
else
alert("Hey. Why didn't you give me a number?");

18

Se tivermos mais que dois caminhos a escolher, mltiplos pares de

if

else

podem ser "encadeados". Aqui

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");

O programa ir primeiramente verificar se

num

menor que 10. Se for, ele escolhe esse caminho, mostra "Small"

e termina sua execuo. Se no for, ele escolhe o caminho

else

, que contm o segundo

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.

O esquema de setas para este programa parece com algo assim:

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

Uma declarao que inicia com a palavra-chave

while

cria um loop. A palavra

expresso entre parnteses e seguida por uma declarao, similar ao

if

while

acompanhada por uma

. O loop continua executando a

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

e loop com chaves. Eles fazem isso tanto para manter a

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

vez que o loop se repete,


nmero

12

number

incrementado por

. No incio de cada repetio, ele comparado com o

para decidir se o programa terminou de executar todo o trabalho esperado.

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

O contador pode tambm iniciar com

e checar o valor com

<= 10

, mas por razes que iremos ver no Captulo

4, uma boa ideia se acostumar a usar a contagem iniciando com zero.


O loop

do

uma estrutura de controle similar ao

. A nica diferena entre eles que o

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

. Primeiramente uma varivel "contadora"

criada para monitorar o progresso do loop. Em seguida, temos o loop

while

que contm uma expresso de

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

for (var number = 0; number <= 12; number = number + 2)


[Link](number);
// 0
// 2
//

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

devem conter dois pontos e vrgulas. A parte anterior ao primeiro ponto

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

Repare que mesmo no abrindo o bloco com

, a declarao no loop continua indentada com dois espaos

para deixar claro que ela "pertence" linha anterior a ela.

Quebrando a execuo de um Loop


Ter uma condio que produza um resultado
declarao especial chamada

break

false

no a nica maneira que um loop pode parar. Existe uma

que tem o efeito de parar a execuo e sair do loop em questo.

Esse programa ilustra o uso da declarao

break

. Ele encontra o primeiro nmero que , ao mesmo tempo,

maior ou igual a 20 e divisvel por 7.


for (var current = 20; ; current++) {
if (current % 7 == 0)
break;
}
[Link](current);
// 21

Usar o operador resto (

) uma maneira fcil de testar se um nmero divisvel por outro. Se for, o resto da

diviso entre eles zero.


A construo do

for

nesse exemplo no contm a parte que checa pelo fim do loop. Isso significa que o loop no

vai parar de executar at que a declarao


Se voc no incluir a declarao
resultado

true

break

contida nele seja executada.

ou acidentalmente escrever uma condio que sempre produza um

break

, seu programa ficar preso em um loop infinito. Um programa preso em um loop infinito nunca vai

terminar sua execuo, o que normalmente uma coisa ruim.


Se voc criar um loop infinito em algum dos exemplos destas pginas, voc normalmente ser perguntado se
deseja interromper a execuo do script aps alguns segundos. Se isso no funcionar, voc dever fechar a aba
que est trabalhando, ou em alguns casos, fechar o navegador para recuper-lo.
A palavra-chave
continue

continue

similar ao

break

, de modo que tambm influencia o progresso de um loop. Quando

encontrado no corpo de um loop, o controle de execuo pula para fora do corpo e continua

executando a prxima iterao do loop.

Atualizando variveis sucintamente


Um programa, especialmente quando em loop, muitas vezes precisa de atualizar uma varivel para armazenar
um valor baseado no valor anterior dessa varivel.
counter = counter + 1;

O JavaScript fornece um atalho para isso:

counter += 1;

22

Atalhos similares funcionam para outros operadores, como

result *= 2

para dobrar o

result

ou

counter -= 1

para diminuir um.


Isto nos permite encurtar nosso exemplo de contagem um pouco mais:
for (var number = 0; number <= 12; number += 2)
[Link](number);

Para

counter += 1

counter -= 1

, existem equivalentes mais curtos:

Resolvendo um valor com

counter++

counter--

switch

comum que o cdigo fique assim:


if (variable == "value1") action1();
else if (variable == "value2") action2();
else if (variable == "value3") action3();
else defaultAction();

H um construtor chamado

switch

que se destina a resolver o envio de valores de uma forma mais direta.

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

parecendo melhor. Aqui est um exemplo:


switch (prompt("What is the weather like?")) {
case "rainy":
[Link]("Remember to bring an umbrella.");
break;
case "sunny":
[Link]("Dress lightly.");
case "cloudy":
[Link]("Go outside.");
break;
default:
[Link]("Unknown weather type!");
break;
}

Dentro do bloco aberto pelo

switch

, voc pode colocar qualquer nmero de rtulo no

para o rtulo correspondente ao valor que

switch

fornece, ou para

default

case

. O programa vai pular

se nenhum valor for encontrado. Ento

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"

(ele recomenda "ir l fora" para ambos os tempos

break

break

, pode ser usado para compartilhar algum cdigo entre os


sunny

cloudy

). Mas tenha cuidado: fcil esquecer de

, 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

, a primeira letra da varivel capitalizada tambm. Isso feito para

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 (

//

) e ento o comentrio aps.

var accountBalance = calculateBalance(account);


// It's a green hollow where a river sings
[Link]();
// Madly catching white tatters in the grass.
var report = new Report();
// Where the sun on the proud mountain rings:
addToReport(accountBalance, report);
// It's a little valley, foaming like light in a glass.

Um

// comentrio

vai at o final da linha. Uma seo de texto entre

/*

*/

ser ignorado, independentemente

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) {}

. Essa chamada de funo uma expresso, que pode produzir um valor.

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.

Tringulo com Loop


Escreva um programa que faa sete chamadas a

[Link]()

para retornar o seguinte tringulo:

#
##
###
####
#####
######
#######

Uma maneira interessante para saber o comprimento de uma

string

escrevendo

.length

aps ela.

var abc = "abc";


[Link]([Link]);
// 3

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

de caracteres. Voc pode ir de 1 para 2

). Voc pode ir de "#" para "##", adicionando um caractere (

acompanhar de perto o nmero, de impresso do programa.

FizzBuzz
25

+ = "#"

). Assim, a soluo pode

Escreva um programa que imprima usando


nmeros divisveis por 3, imprima
Buzz

[Link]()

todos os nmeros de 1 a 100 com duas excees. Para

ao invs do nmero, e para nmeros divisveis por 5 (e no 3), imprima

Fizz

Quando o programa estiver funcionando, modifique-o para imprimir


ambos por 3 e 5 (e continue imprimindo

Fizz

Buzz

FizzBuzz

para nmeros que so divisveis

para nmeros divisveis por apenas um deles).

(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 (

) para verificar se um nmero divisvel por

outro nmero (ter zero de resto).


Na primeira verso, existem trs resultados possveis para cada nmero, ento voc ir criar uma cadeia de
if/else if/else

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]

deve mostrar algo como isto:

# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #

Quando voc tiver o programa que gere este padro, defina a varivel
funcione para qualquer

size

size = 8

e altere programa para que ele

, a sada da grade de largura e altura.

// Your code here.

Dica:
A sequncia pode ser construda iniciando vazia ("") e repetidamente adicionando caracateres. O caracter para
uma nova linha escrito assim
Utilize

[Link]

\n

para visualizar a sada do seu programa.

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

, e como invoc-las. Funes so essenciais na programao

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.

Definindo Uma Funo


Uma definio de funo nada mais do que uma definio normal de uma varivel, na qual o valor recebido pela
varivel uma funo. Por exemplo, o cdigo a seguir define uma varivel

square

que se refere a uma funo que

retorna o quadrado do nmero dado:


var square = function(x) {
return x * x;
};
[Link](square(12));
// 144

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

) e um "corpo", contendo as declaraes que sero

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

var makeNoise = function() {


[Link]("Pling!");
};
makeNoise();
// Pling!
var power = function(base, exponent) {
var result = 1;
for (var count = 0; count < exponent; count++)
result *= base;
return result;
};
[Link](power(2, 10));
// 1024

Algumas funes produzem um valor, como as funes


makeNoise

power

, que produz apenas um efeito colateral. A declarao

square
return

acima, e outras no, como no exemplo de


usada para determinar o valor de

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

dentro do "corpo" de uma funo. Variveis declaradas fora do contexto

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

localmente, portanto sua referncia a

no topo do exemplo:

29

est associada varivel global

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

possui duas funes,

flat

mountain

, declaradas dentro do seu corpo:

var landscape = function() {


var result = "";
var flat = function(size) {
for (var count = 0; count < size; count++)
result += "_";
};
var mountain = function(size) {
result += "/";
for (var count = 0; count < size; count++)
result += "'";
result += "\\";
};
flat(3);
mountain(4);
flat(6);
mountain(1);
flat(1);
return result;
};
[Link](landscape());
// ___/''''\______/'\_

As funes

flat

mountain

podem ver a varivel

result

porque elas esto dentro do mesmo escopo da funo

que as definiu. Entretanto, elas no conseguem ver a varivel

count

uma da outra (somente a sua prpria), pois

elas esto definidas em escopos diferentes. O ambiente externo funo


variveis definidas dentro de

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

dentro do bloco faz referncia mesma varivel fora do bloco. Na realidade,

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

, que funcionar como

var

, mas criar uma varivel que local ao b loco que a contm e no

funo que a contm.

Funes Como Valores


As variveis de funo, normalmente, atuam apenas como nomes para um pedao especfico de um programa.
Tais variveis so definidas uma vez e nunca se alteram. Isso faz com que seja fcil confundir a funo com seu
prprio nome.
Entretanto, so duas coisas distintas. Um valor de funo pode fazer todas as coisas que outros valores podem
fazer - voc pode us-lo em expresses arbitrrias e no apenas invoc-la. possvel armazenar um valor de
funo em um novo local, pass-lo como argumento para outra funo e assim por diante. No muito diferente,
uma varivel que faz referncia a uma funo continua sendo apenas uma varivel regular e pode ser atribuda a
um novo valor, como mostra o exemplo abaixo:
var launchMissiles = function(value) {
[Link]("now");
};
if (safeMode)
launchMissiles = function(value) {/* do nothing */};

No captulo 5, ns vamos discutir as coisas maravilhosas que podem ser feitas quando passamos valores de
funo para outras funes.

Notao Por Declarao


Existe uma maneira mais simples de expressar

var square = function

ser usada no incio da declarao, como demonstrado abaixo:

31

. A palavra-chave

function

tambm pode

function square(x) {
return x * x;
}

Isso uma declarao de funo. Ela define a varivel

square

e faz com que ela referencie a funo em questo.

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");

A execuo desse programa funciona da seguinte forma: a chamada funo


para o incio dessa funo (linha 2). Em seguida, invocado

[Link]

greet

faz com que o controle pule

(uma funo embutida no navegador),

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

executa uma nova chamada a

[Link]

Podemos representar o fluxo de controle, esquematicamente, assim:

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]

retorna o controle para a funo

greet

. No outro caso, ela retorna para o final do programa.

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

pode ser chamada com um ou dois argumentos. No caso de ser invocada

com apenas um argumento, ela assumir o valor 2 para o expoente e a funo se comportar com um expoente
ao quadrado.

33

function power(base, exponent) {


if (exponent == undefined)
exponent = 2;
var result = 1;
for (var count = 0; count < exponent; count++)
result *= base;
return result;
}
[Link](power(4));
// 16
[Link](power(4, 3));
// 64

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]

tira proveito disso, imprimindo todos os valores que foram passados.

[Link]("R", 2, "D", 2);


// R 2 D 2

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

que cria uma varivel local e

retorna uma funo que acessa e retorna essa varivel.


function wrapValue(n) {
var localVariable = n;
return function() { return localVariable; };
}
var wrap1 = wrapValue(1);
var wrap2 = wrapValue(2);
[Link](wrap1());
// 1
[Link](wrap2());
// 2

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

no necessria, pois o parmetro em si j

uma varivel local.


Pensar em programas que funcionam dessa forma requer um pouco de prtica. Um bom modelo mental
pensar que a palavra-chave

function

valor da funo). Quando voc ler

"congela" o cdigo que est em seu corpo e o envolve em um pacote (o

return function(...) {...}

, pense como se estivesse retornando um

manipulador que possibilita executar instrues computacionais que foram "congeladas" para um uso posterior.
No exemplo,

multiplier

retorna um pedao de cdigo "congelado" que fica armazenado na varivel

twice

.A

ltima linha do exemplo chama o valor armazenado nessa varivel, fazendo com que o cdigo "congelado" (
number * factor;
multiplier
number

) seja executado. Ele continua tendo acesso varivel

factor

return

que foi criada na chamada de

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

function power(base, exponent) {


if (exponent == 0)
return 1;
else
return base * power(base, exponent - 1);
}
[Link](power(2, 3));
// 8

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

, a verso menos elegante (usando lao de repetio)

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

simples acaba se tornando uma escolha atrativa.


A regra bsica que tem sido repetida por muitos programadores e com a qual eu concordo plenamente, no se
preocupar com eficincia at que voc saiba, com certeza, que o programa est muito lento. Quando isso
acontecer, encontre quais partes esto consumindo maior tempo de execuo e comece a trocar elegncia por
eficincia nessas partes.
evidente que essa regra no significa que se deva ignorar a performance completamente. Em muitos casos,
como na funo

, no h muitos benefcios em usar a abordagem mais elegante. Em outros casos, um

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

, ela retornada. Caso contrrio, a segunda chamada

retornada, independentemente se ela produzir string ou

null

Para entender melhor como essa funo produz o resultado que estamos esperando, vamos analisar todas as
chamadas a

find

que so feitas quando procuramos a soluo para o nmero 13.

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!

A indentao reflete a profundidade da pilha de chamadas. A primeira chamada do


vezes, explorando as solues que comeam com
que comea com

(1 + 5)

(1 + 5)

(1 * 3)

find

invoca a si mesma duas

. A primeira chamada tenta achar a soluo

e, usando recurso, percorre todas as possveis solues que produzam um nmero

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

faz com que a pilha de

. Essa busca tem resultados satisfatrios,

porque aps duas chamadas recursivas acaba encontrando o nmero 13. Essa chamada recursiva mais interna
retorna uma

string

e cada operador

||

nas chamadas intermedirias passa essa

string

adiante, retornando

no final a soluo esperada.

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

aps o valor de uma

. Por isso, o lao de repetio

string

while

nos fornecer o tamanho (quantidade de caracteres) daquela

continua adicionando zeros no incio da

string

que representa o

nmero at que a mesma tenha trs caracteres.


Misso cumprida! Porm, no momento em que iramos enviar o cdigo ao fazendeiro (juntamente com uma
grande cobrana, claro), ele nos ligou dizendo que comeou a criar porcos, e perguntou, se poderamos
estender a funcionalidade do software para tambm imprimir os porcos?
claro que podemos. Antes de entrar no processo de copiar e colar essas mesmas quatro linhas outra vez,
vamos parar e reconsiderar. Deve existir uma forma melhor. Aqui est a primeira tentativa:
function printZeroPaddedWithLabel(number, label) {
var numberString = String(number);
while ([Link] < 3)
numberString = 0 + numberString;
[Link](numberString + + label);
}
function printFarmInventory(cows, chickens, pigs) {
printZeroPaddedWithLabel(cows, Cows);
printZeroPaddedWithLabel(chickens, Chickens);
printZeroPaddedWithLabel(pigs, Pigs);
}
printFarmInventory(7, 11, 3);

Funcionou! Mas o nome

printZeroPaddedWithLabel

um pouco estranho. Ele uma combinao de trs coisas -

imprimir, adicionar zeros e adicionar a label correta - em uma nica funo.


Ao invs de tentarmos abstrair a parte repetida do nosso programa como um todo, vamos tentar selecionar
apenas um conceito.
function zeroPad(number, width) {
var string = String(number);
while ([Link] < width)
string = 0 + string;
return string;
}
function printFarmInventory(cows, chickens, pigs) {
[Link](zeroPad(cows, 3) + Cows);
[Link](zeroPad(chickens, 3) + Chickens);
[Link](zeroPad(pigs, 3) + Pigs);
}
printFarmInventory(7, 16, 3);

38

Ter uma funo com um bom nome descritivo como

zeroPad

torna fcil para qualquer um ler e entender o cdigo.

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.

Funes e Efeitos Colaterais


Funes podem ser divididas naquelas que so invocadas para produzir um efeito colateral e naquelas que so
invocadas para gerar um valor de retorno (embora tambm seja possvel termos funes que produzam efeitos
colaterais e que retornem um valor).
A primeira funo auxiliar no exemplo da fazenda,
colateral: imprimir uma linha. A segunda verso,

printZeroPaddedWithLabel
zeroPad

, invocada para produzir um efeito

, chamada para produzir um valor de retorno. No

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

certamente til. Algumas operaes so tambm mais

[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

// Create a function value f


var f = function(a) {
[Link](a + 2);
};
// Declare g to be a function
function g(a, b) {
return a * b * 3.5;
}

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]

que retorna o seu menor argumento. Ns podemos reproduzir

essa funcionalidade agora. Escreva uma funo

min

que recebe dois argumentos e retorna o menor deles.

// Your code here.


[Link](min(0, 10));
// 0
[Link](min(0, -10));
// -10

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

que satisfaa as condies descritas acima. A funo deve aceitar um

nmero como parmetro e retornar um valor Booleano.


Teste-a com os valores 50 e 75. Observe como ela se comporta com o valor -1. Por qu? Voc consegue pensar
em uma maneira de arrumar isso?

40

// Your code here.


[Link](isEven(50));
// true
[Link](isEven(75));
// false
[Link](isEven(-1));
// ??

Dica: Sua funo ser semelhante funo interna


uma cadeia de declaraes

if

else if

else

find

do exemplo recursivo

findSolution

que testam qual dos trs casos se aplica. O

neste captulo, com


else

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

. O valor retornado ser uma

string

"string".charAt(N)

, similar a como

contendo somente um caractere

). O primeiro caractere est na posio zero, o que faz com que o ltimo seja encontrado na

[Link] -1

. Em outras palavras, uma

suas respectivas posies so


Escreva uma funo

countBs

string

com dois caracteres possui tamanho (

length

) dois, e

que receba uma

string

como nico argumento e retorna o nmero que indica

quantos caracteres "B", em maisculo, esto presentes na


Em seguida, escreva uma funo chamada

countChar

string

que se comporta de forma parecida com

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

para fazer essa nova funcionalidade.

// Your code here.


[Link](countBs(BBC));
// 2
[Link](countChar(kakkerlak, k));
// 4

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

). Quando o lao chegar ao seu fim, a varivel

counter

dever ser retornada.

Certifique-se de usar e criar variveis locais funo, utilizando a palavra-chave

41

var

Estrutura de dados: Objetos e Array


Em duas ocasies me perguntaram: "Ora, Sr. Babbage, se voc colocar nmeros errados em uma
mquina, repostas certas iro sair?" [...] Certamente eu no sou capaz de compreender o tipo de confuso
de ideias que poderia provocar tal questionamento.
Charles Babbage, Passages from the Life of a Philosopher (1864)
Nmeros, Booleanos e strings so os tijolos usados para construir as estruturas de dados. Entretanto, voc no
consegue fazer uma casa com um nico tijolo. Objetos nos permitem agrupar valores (incluindo outros objetos) e,
consequentemente, construir estruturas mais complexas.
Os programas que construmos at agora foram seriamente limitados devido ao fato de que estiveram operando
apenas com tipos de dados simples. Esse captulo ir adicionar uma compreenso bsica sobre estrutura de
dados para o seu kit de ferramentas. Ao final, voc saber o suficiente para comear a escrever programas teis.
O captulo ir trabalhar com um exemplo de programao mais ou menos realista, introduzindo conceitos a
medida em que eles se aplicam ao problema em questo. O cdigo de exemplo ser, muitas vezes, construdo
em cima de funes e variveis que foram apresentadas no incio do texto.

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

alguma forma, extrair os dgitos e convert-los em nmeros para poder acess-los.


Felizmente, o JavaScript fornece um tipo de dado especfico para armazenar uma sequncias de valores. Ele
chamado de array e escrito como uma lista de valores separados por vrgulas e entre colchetes.
var listOfNumbers = [2, 3, 5, 7, 11];
[Link](listOfNumbers[1]);
// 3
[Link](listOfNumbers[1 - 1]);
// 2

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]

. Se voc no tem experincia com programao, essa conveno pode levar

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]

acessam uma propriedade em algum valor. No primeiro caso, acessamos a propriedade


em

myString

[Link]

(para

(funo que retorna o valor mximo). Essas so expresses que

. No segundo, acessamos a propriedade chamada

max

no objeto

Math

length

do valor contido

(que um conjunto de

valores e funes relacionados matemtica).


Praticamente todos os valores no JavaScript possuem propriedades. As nicas excees so
Se voc tentar acessar a propriedade em algum deles, voc receber um erro.
[Link];
// TypeError: Cannot read property 'length' of null

43

null

undefined

As duas formas mais comuns de acessar propriedades no JavaScript so usando ponto e colchetes. Ambos
value.x

value[x]

acessam uma propriedade em

diferena est em como o

value

, mas no necessariamente a mesma propriedade. A

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]

tenta avaliar a expresso

value.x

acessa a propriedade chamada

e, ento, usa o seu resultado como o nome da propriedade.

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

, um nmero de propriedades que se

referem valores de funo.


var doh = "Doh";
[Link](typeof [Link]);
// function
[Link]([Link]());
// DOH

Toda string possui uma propriedade

toUpperCase

. Quando chamada, ela retornar uma cpia da string com todas

as letras convertidas para maisculas. Existe tambm a propriedade

toLowerCase

, que voc j pode imaginar o

que faz.
Curiosamente, mesmo que a chamada para
funo tem acesso string

"Doh"

toUpperCase

no passe nenhum argumento, de alguma forma a

, que o valor em que a propriedade foi chamada. Como isso funciona

exatamente descrito no Captulo 6.


As propriedades que contm funes so geralmente chamadas de mtodos do valor a que pertencem. Como
por exemplo, "

toUpperCase

um mtodo de uma string".

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

pode ser usado para adicionar valores ao final de um array. O mtodo

pop

faz o contrrio, remove

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

. O argumento passado para

join

determina o texto que ser inserido entre cada elemento do

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

, o que acontece quando tentamos

no exemplo anterior.

possvel atribuir um valor a uma propriedade usando o operador

. Isso ir substituir o valor de uma

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"

. Voc pode interpret-los como polvos com longos tentculos de

tamanhos semelhantes, ordenados em linha e rotulados com nmeros.

46

Portanto, podemos representar o dirio de Jacques como um array de objetos.


var journal = [
{events: ["work", "touched tree", "pizza",
"running", "television"],
squirrel: false},
{events: ["work", "ice cream", "cauliflower",
"lasagna", "touched tree", "brushed teeth"],
squirrel: false},
{events: ["weekend", "cycling", "break",
"peanuts", "beer"],
squirrel: true},
/* and so on... */
];

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"

, impossvel que outro cdigo altere os caracteres dessa

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

esto associadas ao mesmo ob jeto e, por isso, alterar

object3

object1

tambm altera o

aponta para um objeto diferente, o qual inicialmente contm as mesmas

e sua existncia totalmente separada.

Quando comparamos objetos, o operador

==

do JavaScript ir retornar

true

apenas se ambos os objetos

possuem exatamente o mesmo valor. Comparar objetos diferentes ir retornar

false

mesmo se eles tiverem

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

pode ser calculado usando a seguinte frmula, onde n se refere tabela:


= (n11n00 - n10n01) / n1n0n1n0
[TODO: Adicionar formatao correta da frmula aps converter em asciidoc]

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

com dois elementos cada (


"01"

[[76, 9], [4, 1]]

), ou at mesmo um objeto com propriedades nomeadas de

"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 refere ao caso no qual Jacques

se tornou um esquilo, mas o evento no ocorreu (por exemplo "pizza"). Isso aconteceu quatro vezes, e j que o
nmero binrio

10

equivalente ao nmero 2 na notao decimal, iremos armazenar esse valor no ndice 2 do

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

Essa simplesmente uma traduo direta da frmula de para o JavaScript.


raiz quadrada, fornecida pelo objeto

Math

[Link]

a funo que calcula a

que padro do JavaScript. Temos que somar dois campos da tabela

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

e em um arquivo que pode ser baixado.

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

testa se uma entrada contm ou no o evento em questo. Os arrays possuem um mtodo

que procura pelo valor informado no array (nesse exemplo o nome do evento), e retorna o ndice onde ele

foi encontrado ou -1 se no for. Portanto, se a chamada de

indexOf

no retornar -1, sabemos que o evento foi

encontrado.
O corpo do loop presente na funo

tableFor

, descobre qual caixa da tabela cada entrada do dirio pertence,

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?

Objetos como mapas

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

tal propriedade existe.


var map = {};
function storePhi(event, phi) {
map[event] = phi;
}
storePhi("pizza", 0.069);
storePhi("touched tree", -0.081);
[Link]("pizza" in map);
// true
[Link](map["touched tree"]);
// -0.081

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

e se distingue pelo fato de utilizar a palavra

in

for (var event in map)


[Link]("The correlation for '" + event +
"' is " + map[event]);
// The correlation for 'pizza' is 0.069
// The correlation for 'touched tree' is -0.081

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

Vamos ver o resultado.


for (var event in correlations)
[Link](event + ": " + correlations[event]);
// carrot:

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

// brushed teeth: -0.3805211953


// candy:

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.

Estudo aprofundado de Arrays


Antes de finalizar esse captulo, gostaria de introduzir alguns outros conceitos relacionados a objetos.
Comearemos com alguns mtodos normalmente teis dos arrays.
Vimos no incio do captulo os mtodos

push

pop

, que adicionam e removem elementos no final de um array.

Os mtodos correspondentes para adicionar e remover itens no incio de um array so chamados


shift

unshift

var todoList = [];


function rememberTo(task) {
[Link](task);
}
function whatIsNext() {
return [Link]();
}
function urgentlyRememberTo(task) {
[Link](task);
}

O programa anterior gerencia uma lista de tarefas. Voc pode adicionar tarefas no final da lista chamando
rememberTo("eat")

e, quando estiver preparado para realizar alguma tarefa, voc chama

e remover o primeiro item da lista. A funo

urgentlyRememberTo

whatIsNext()

para acessar

tambm adiciona uma tarefa, porm, ao invs de

adicionar ao final da lista, a adiciona no incio.


O mtodo

indexOf

tem um irmo chamado

lastIndexOf

, que comea a pesquisa de um dado elemento pelo final

do array ao invs de comear pelo incio.


[Link]([1, 2, 3, 2, 1].indexOf(2));
// 1
[Link]([1, 2, 3, 2, 1].lastIndexOf(2));
// 3

Ambos

indexOf

lastIndexOf

recebem um segundo argumento opcional que indica onde iniciar a pesquisa.

Outro mtodo fundamental o

slice

, que recebe um ndice de incio e outro de parada, retornando um array que

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

Quando o ndice de parada no informado, o


tambm possuem o mtodo
O mtodo

concat

slice

ir pegar todos os elementos aps o ndice de incio. Strings

com um comportamento similar.

slice

pode ser usado para unir arrays, parecido com o que o operador

exemplo a seguir mostra ambos

concat

slice

faz com as strings. O

em ao. Ele recebe um array e um ndice como argumento,

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"]

Strings e suas propriedades


Podemos ler propriedades como

length

toUpperCase

de strings. Porm, caso tente adicionar uma nova

propriedade, ela no ser adicionada.


var myString = "Fido";
[Link] = "value";
[Link]([Link]);
// undefined

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

, que so parecidos com os mtodos de array que

possuem o mesmo nome.


[Link]("coconuts".slice(4, 7));
// nut
[Link]("coconut".indexOf("u"));
// 5

Uma diferena que o

indexOf

das strings pode receber uma string contendo mais de um caractere, enquanto

que o mtodo correspondente no array procura apenas por um nico elemento.


[Link]("one two three".indexOf("ee"));
// 11

O mtodo

trim

remove todos os espaos vazios (espaos, linhas, tabs e caracteres similares) do comeo e do

final de uma string.


[Link]("

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

var string = "abc";


[Link]([Link]);
// 3
[Link]([Link](0));
// a
[Link](string[1]);
// b

O Objeto Arguments
Sempre que uma funo invocada, uma varivel especial chamada

arguments

adicionada ao ambiente no qual

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

que nos informa o nmero de argumentos que realmente foi

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

), fazendo com que seja um

pouco mais difcil de se usar do que um array de verdade.


function argumentCounter() {
[Link]("You gave me", [Link], "arguments.");
}
argumentCounter("Straw man", "Tautology", "Ad hominem");
// You gave me 3 arguments.

Algumas funes podem receber qualquer nmero de argumentos, como no caso de


normalmente percorrem por todos os valores em seu objeto

arguments

[Link]

. Essas funes

e podem ser usadas para criar interfaces

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");

Essa verso l o primeiro argumento (

squirrel

) da forma normal e depois percorre o resto dos argumentos (o

loop pula o primeiro argumento, iniciando no ndice 1) juntando-os em um array.

55

O Objeto Math
Como vimos anteriormente,
(mximo),

[Link]

O objeto

Math

[Link]

Math

uma caixa de ferramentas com funes relacionadas a nmeros, tais como

(mnimo) e

[Link]

(raiz quadrada).

usado como um container para agrupar uma srie de funcionalidades relacionadas. Existe

apenas um nico objeto

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

em um de seus programas. Sabendo que no JavaScript a funo nativa

segura dentro do objeto

Math

est contida de forma

max

, no precisamos nos preocupar em sobrescrev-la.

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

, caso precise realizar clculos trigonomtricos,

(seno) e

tan

Math

pode ajud-lo. Ele contm

(tangente), tanto quanto suas funes inversas

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]

. (Existe uma tradio antiga na programao de escrever os

nomes de valores constantes em caixa alta).


function randomPointOnCircle(radius) {
var angle = [Link]() * 2 * [Link];
return {x: radius * [Link](angle),
y: radius * [Link](angle)};
}
[Link](randomPointOnCircle(2));
// {x: 0.3667, y: 1.966}

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]

. Essa uma funo que retorna um nmero "pseudo-aleatrio" entre zero

(includo) e um (excludo) toda vez que voc a chama.


[Link]([Link]());
// 0.36993729369714856
[Link]([Link]());
// 0.727367032552138
[Link]([Link]());
// 0.40180766698904335

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,

qualquer nmero de zero a nove.


Tambm existem as funes
[Link]

[Link]

(para arredondar o valor para o maior nmero inteiro mais prximo) e

(para arredondar o valor para o nmero inteiro mais prximo).

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

var myVar = 10;


[Link]("myVar" in window);
// true
[Link]([Link]);
// 10

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

, a maioria dos valores no JavaScript possuem propriedades e so acessados

value["propName"]

. Objetos tendem a usar nomes para suas propriedades e

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

e uma srie de mtodos.

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

for (var name in object)

) para percorrer todas as propriedades do objeto.

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

Escreva uma funo chamada

que recebe dois argumentos,

range

contendo todos os nmeros a partir do valor


Em seguida, escreva a funo

start

at o valor

end

(incio) e

start

end

(fim), e retorna um array

(incluindo-o).

que recebe um array de nmeros como argumento e retorna a soma desses

sum

nmeros. Execute o programa anterior e veja se o resultado retornado de fato 55.


Como exerccio bnus, modifique a sua funo

range

para aceitar um terceiro argumento opcional que indica o

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

valores negativos, fazendo com que

[1, 3, 5, 7, 9]

range(5, 2, -1)

produza

. Certifique-se de que funcione tambm com

[5, 4, 3, 2]

// Your code here.


[Link](range(1, 10));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[Link](range(5, 2, -1));
// [5, 4, 3, 2]
[Link](sum(range(1, 10)));
// 55

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

[]

(um novo array

para adicionar os valores. No se esquea de

retornar o array no final da funo.


Devido ao fato de que o limite final inclusivo, ao invs de usar um simples operador
operador

<=

<

, voc dever usar o

para checar o final do seu loop.

Para verificar se o argumento opcional de incremento foi fornecido, voc pode verificar o
comparar o valor do argumento com

undefined

[Link]

ou

. Caso no tenha sido informado, apenas configure o seu valor

padro (1) no incio da funo.


Fazer com que

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)

retorna algo relevante.

Invertendo um array
Os arrays possuem o mtodo

reverse

, que modifica o array invertendo a ordem em que os elementos aparecem.

Para esse exerccio, escreva duas funes:

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

) funciona da mesma forma que o mtodo

reverse

, s que nesse caso, invertendo

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

// Your code here.


[Link](reverseArray(["A", "B", "C"]));
// ["C", "B", "A"];
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
[Link](arrayValue);
// [5, 4, 3, 2, 1]

Dicas
Existem duas maneiras bvias de implementar
do incio ao fim e usar o mtodo

unshift

reverseArray

. A primeira simplesmente iterar o array fornecido

para inserir cada elemento no incio do novo array. A segunda iterar o

array fornecido comeando pelo fim e terminando no incio, usando o mtodo


frente faz com que seja necessrio usar uma notao
-

push

um pouco estranha (

. Iterar um array de trs para

var i = [Link] - 1; i >= 0; i-

).

Inverter o array em questo (

reverseArrayInPlace

) mais difcil. Voc deve ter cuidado para no sobrescrever

elementos que voc precisar posteriormente. Usar


(

for

[Link](0)

reverseArray

ou at mesmo copiar o array inteiro

uma boa forma de se copiar um array) funciona, mas considerado trapaa.

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]

para arredondar o valor para

baixo, pois voc no precisa lidar com o elemento do meio de um array com tamanho mpar) e substituir o
elemento na posio

com o elemento na posio

[Link] - 1 - i

. Voc pode usar uma varivel local para

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
}
}
};

O resultado uma cadeia de objetos conectados, como mostrado abaixo:

Uma das vantagens das listas que elas podem compartilhar partes de sua estrutura. Por exemplo, se eu
criasse dois novos valores

{value: 0, rest: list}

{value: -1, rest: list}

(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

como argumento e, escreva tambm, a funo

Alm disso, implemente uma funo auxiliar

prepend

que produz um array a partir de uma lista.

listToArray

que receber um elemento e uma lista e ser responsvel

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

caso no exista elemento em tal posio.

Caso no tenha feito ainda, implemente a verso recursiva da funo

nth

// Your code here.


[Link](arrayToList([10, 20]));
// {value: 10, rest: {value: 20, rest: null}}
[Link](listToArray(arrayToList([10, 20, 30])));
// [10, 20, 30]
[Link](prepend(10, prepend(20, null)));
// {value: 10, rest: {value: 20, rest: null}}
[Link](nth(arrayToList([10, 20, 30]), 1));
// 20

Dicas
Construir uma lista mais fcil de ser feito de trs para frente. Portanto,

arrayToList

poderia percorrer o array de

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}

list = {value: X, rest:

para adicionar um elemento.

Para percorrer uma lista (no caso de

listToArray

nth

), o seguinte loop

for

pode ser usado:

for (var node = list; node; node = [Link]) {}

Voc consegue ver como isso funcionaria? A cada iterao do loop,


isso, o corpo da funo pode acessar a propriedade
node

value

node

aponta para a prxima sublista e, por

para pegar o elemento atual. Ao final de cada iterao,

atualizado apontando para a prxima sublista. Quando seu valor

null

, ns chegamos ao final da lista e

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

lista, voc pode simplesmente acessar a propriedade


+ 1

value

do seu n head (inicial). Para acessar o elemento

, voc pega o n-simo elemento da lista que est contido na propriedade

rest

da lista em questo.

Comparao "profunda"
O operador

==

compara objetos pelas suas identidades. Entretanto, algumas vezes, voc pode preferir comparar

os valores das suas propriedades de fato.


Escreva a funo

deepEqual

que recebe dois valores e retorna

true

apenas se os valores forem iguais ou se

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

exceo: devido a um acidente histrico,

typeof null

tambm produz

"object"

// Your code here.


var obj = {here: {is: "an"}, object: 2};
[Link](deepEqual(obj, obj));
// true
[Link](deepEqual(obj, {here: 1, object: 2}));
// false
[Link](deepEqual(obj, {here: {is: "an"}, object: 2}));
// true

Dicas
O teste para saber se est lidando com um objeto real dever ser parecido com

typeof x == "object" && x != null

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

se forem diferentes. Caso seja o

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

Para retornar o valor correto da funo, mais fcil retornar imediatamente


encontrada e retornar

true

apenas ao final da funo.

61

false

quando qualquer diferena for

Funes de ordem superior


"Tzu-li e Tzu-ssu estavam se gabando do tamanho dos seus ltimos programas. 'Duzentas mil linhas sem
contar os comentrios!', disse Tzu-li. Tzu-ssu respondeu: 'Pssh, o meu j tem quase um milho de linhas'.
Mestre Yuan-Ma disse: 'Meu melhor programa tem quinhentas linhas'. Ouvindo isso, Tzu-li e Tzu-ssu ficaram
esclarecidos."
Master Yuan-Ma, The Book of Programming
"Existem duas maneiras de construir o design de um software: uma maneira deix-lo to simples de tal
forma em que obviamente no h deficincias, e a outra torn-lo to complicado que no haver
deficincias bvias."
C.A.R. Hoare, 1980 ACM Turing Award Lecture
Um programa grande um programa custoso, e no necessariamente devido ao tempo que leva para construir.
Tamanho quase sempre envolve uma complexidade e complexidade confunde os programadores.
Programadores confusos tendem a criar erros (bugs) no programa. Um programa grande tem a possibilidade de
esconder bugs que so difceis de serem encontrados.
Vamos rapidamente abordar dois exemplos que foram citados na introduo. O primeiro contm um total de 6
linhas.
var total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
}
[Link](total);

O segundo necessita de duas funes externas e escrito em apenas uma linha.


[Link](sum(range(1, 10)));

Qual mais propenso a erros?


Se medirmos o tamanho das definies de

sum

range

, o segundo programa tambm ser grande - at maior

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

) ainda assim tero que lidar com laos de repetio,

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

apareceu vrias vezes:

var array = [1, 2, 3];


for (var i = 0; i < [Link]; i++) {
var current = array[i];
[Link](current);
}

O que ele diz : "Para cada elemento do array, registre no


uma varivel contadora, uma checagem do tamanho do

console

array

". Mas utiliza um jeito redundante que envolve

e a declarao de uma varivel extra para pegar o

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]

para cada elemento:

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

function forEach(array, action) {


for (var i = 0; i < [Link]; i++)
action(array[i]);
}
forEach(["Wampeter", "Foma", "Granfalloon"], [Link]);
// Wampeter
// Foma
// Granfalloon

Normalmente voc no ir passar uma funo predefinida para o

forEach

, mas ela ser criada localmente dentro

da funo.
var numbers = [1, 2, 3, 4, 5], sum = 0;
forEach(numbers, function(number) {
sum += number;
});
[Link](sum);
// 15

Isso parece muito com um

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

que precisamos fechar com chave e parntese.


Nesse padro, podemos simplificar o nome da varivel (
ter que busc-lo fora do

array

De fato, no precisamos definir um mtodo


Quando um

array

number

) pelo elemento atual, ao invs de simplesmente

manualmente.
forEach

. Ele esta disponvel como um mtodo padro em

fornecido para o mtodo agir sobre ele, o

forEach

arrays

a funo a ser executada para cada elemento.


Para ilustrar o quo til isso , vamos lembrar da funo que vimos no captulo anterior, onde continha dois
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

faz parecer levemente menor e bem menos confuso.

function gatherCorrelations(journal) {
var phis = {};
[Link](function(entry) {
[Link](function(event) {
if (!(event in phis))
phis[event] = phi(tableFor(event, journal));
});
});
return phis;
}

64

espera apenas um argumento obrigatrio:

Funes de ordem superior


Funes que operam em outras funes, seja ela apenas devolvendo argumentos, so chamadas de funes de
ordem superior. Se voc concorda com o fato de que as funes so valores normais, no h nada de notvel
sobre o fato de sua existncia. O termo vem da matemtica onde a distino entre funes e outros valores
levado mais a srio.
Funes de ordem superior nos permitem abstrair as aes. Elas podem serem de diversas formas. Por
exemplo, voc pode ter funes que criam novas funes.
function greaterThan(n) {
return function(m) { return m > n; };
}
var greaterThan10 = greaterThan(10);
[Link](greaterThan10(11));
// true

E voc pode ter funes que alteram outras funes.


function noisy(f) {
return function(arg) {
[Link]("calling with", arg);
var val = f(arg);
[Link]("called with", arg, "- got", val);
return val;
};
}
noisy(Boolean)(0);
// calling with 0
// called with 0 - got false

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

um parmetro da funo externa. Mas como as funes internas

esto dentro do ambiente externo, podemos usar a varivel

. Os "corpos" de tais funes internas podem

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

para a funo interna (

arg1

arg2

, e assim por diante) e passar elas para

, mas mesmo assim isso no

deixaria explcito quantos seriam suficientes. Essa soluo limita algumas informaes de
[Link]

como por exemplo

. Sempre passaremos a mesma quantidade de argumentos, mas nunca saberemos a

quantidade exata de argumentos que foi passada.


Para esse tipo de situao, funes em JavaScript possuem um mtodo chamado
(ou um

array

como

objeto

apply

. Voc passa um

array

) como argumento, e ele ir chamar a funo com estes argumentos.

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

e retorna, apenas estes argumentos, para


apply

. O primeiro argumento do

apply

. Ela faz isso passando seus prprios argumentos

, estamos passando

null

, isto pode ser usado para simular

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

Este formato chamado de

JSON

(pronuncia-se "Jason") que significa JavaScript Ob ject Notation.

JSON

amplamente utilizado como armazenamento de dados e formato de comunicao na Web .


JSON

se escreve semelhantemente como

arrays

e objetos em JavaScript, mas com algumas restries. Todos

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

JavaScript nos fornece duas funes

[Link]

[Link]

, que convertem dados para este formato. O

primeiro recebe um valor em JavaScript e retorna uma string codificada em

JSON

. A segunda obtm uma

string

converte-a para um valor que ele codifica.


var string = [Link]({name: "X", born: 1980});
[Link](string);
// {"name":"X","born":1980}
[Link]([Link](string).born);
// 1980

O varivel

est disponvel na

ANCESTRY_FILE

do meu arquivo

como uma

JSON

string

sandbox

deste captulo para download no site, onde est o contedo

. Vamos decodific-lo e ver quantas pessoas contm.

var ancestry = [Link](ANCESTRY_FILE);


[Link]([Link]);
// 39

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", }, ]

Este utiliza um argumento chamado de


computao. A funo
includo no

array

test

test

, com um valor de funo, para preencher uma lacuna na

chamada para cada elemento, e o seu valor de retorno determina se um elemento

retornado.

Trs pessoas no arquivo estavam vivas e jovens em 1924: meu av, minha av e minha tia-av.
Observe como a funo

filter

, em vez de excluir os elementos do

array

, constri um novo com apenas os

elementos que passaram no teste. Esta funo primitiva. No modifica o


Assim como

forEach

filter

um mtodo padro de

arrays

que foi dado.

. O exemplo define uma funo s para mostrar o

que ela faz internamente. A partir de agora vamos us-lo assim:

67

array

[Link]([Link](function(person) {
return [Link] == "Carel Haverbeke";
}));
// [{name: "Carolus Haverbeke", }]

Transformando com map


Digamos que temos um

de objetos que representam pessoas, produzido atravs do

array

de alguma forma. Mas queremos um


O mtodo

map

transforma um

array

de ancestrais

de nomes o que mais fcil para ler.

array

aplicando uma funo para todos os elementos e constri um novo

array

partir dos valores retornados. O novo

array

ter o mesmo tamanho do

array

enviado, mas seu contedo

array

mapeado para um novo formato atravs da funo.


function map(array, transform) {
var mapped = [];
for (var i = 0; i < [Link]; i++)
[Link](transform(array[i]));
return mapped;
}
var overNinety = [Link](function(person) {
return [Link] - [Link] > 90;
});
[Link](map(overNinety, function(person) {
return [Link];
}));
// ["Clara Aernoudts", "Emile Haverbeke",
//

"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

tambm um mtodo padro de

map

arrays

Resumindo com reduce


Outro padro na computao em

arrays

calcular todos elementos e transform-los em apenas um. No nosso

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

funo menos simples do que o

so, alm do

filter

map

array

, uma funo para combinao e um valor inicial. Esta

por isso observe com muita ateno.

68

function reduce(array, combine, start) {


var current = start;
for (var i = 0; i < [Link]; i++)
current = combine(current, array[i]);
return current;
}
[Link](reduce([1, 2, 3, 4], function(a, b) {
return a + b;
}, 0));
// 10

array

padro do mtodo

reduce

que corresponde a esta funo tem uma maior comodidade. Se o seu

array

contm apenas um elemento, voc no precisa enviar um valor inicial. O mtodo ir pegar o primeiro elemento do
array

como valor inicial, comeando a reduo a partir do segundo.

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

( um pouco bobo termos que definir

plus

como uma funo, mas os operadores em JavaScript, diferentemente

das funes, no so valores, ento no podemos passar nenhum argumento.)


69

Ao invs de juntar toda a lgica em um

gigante, ele est bem composto nos conceitos que interessamos

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

mais elegante expresso em uma sequncia separada onde cada passo

e produzir um novo

array

. Mas a construo de todos esses

intermedirios

arrays

um pouco custoso.
Passar uma funo para

forEach

e deixar que o mtodo cuide da iterao para os ns conveniente e fcil de ler.

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

interno acaba rodando

o nmero de vezes que o

interno tiver outro

loop

loop

que realize

loop
P

externo chamando uma funo que executa um


NxM

vezes, onde

o nmero de vezes que o

interno se repete dentro de cada interao do

voltas, seu bloco rodar

M x N x P

loop

loop

loop

loop

dentro

interno),

de fora se

externo. Se esse

vezes e assim por diante. Isto

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.

O pai do pai do pai do pai


Meu av, Philibert Haverbeke est includo nos dados do arquivo. Comeando com ele, eu posso traar minha
linhagem para descobrir qual a pessoa mais velha no conjunto de dados, Pauwels van Haverbeke, meu
ancestral direto. E se ele for, gostaria de saber o quanto de DNA, teoricamente, que partilho com ele.
Para ser capaz de fazer uma busca pelo nome de um pai para um objeto real que representa uma pessoa,
primeiramente precisamos construirmos um objeto que associa os nomes com as pessoas.

70

var byName = {};


[Link](function(person) {
byName[[Link]] = person;
});
[Link](byName["Philibert Haverbeke"]);
// {name: "Philibert Haverbeke", }

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

condensa o valor a partir de uma rvore genealgica.

function reduceAncestors(person, f, defaultValue) {


function valueFor(person) {
if (person == null)
return defaultValue;
else
return f(person, valueFor(byName[[Link]]),
valueFor(byName[[Link]]));
}
return valueFor(person);
}

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

na qual devolve o valor real para essa pessoa.

Podemos ento usar isso para calcular a quantidade de DNA que meu av compartilhou com Pauwels van
Haverbeke e depois dividir por quatro.

71

function sharedDNA(person, fromMother, fromFather) {


if ([Link] == "Pauwels van Haverbeke")
return 1;
else
return (fromMother + fromFather) / 2;
}
var ph = byName["Philibert Haverbeke"];
[Link](reduceAncestors(ph, sharedDNA, 0) / 4);
// 0.00049

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

. Mas separando a abordagem

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

d-nos uma pea til para

trabalhar com o vocabulrio da estrutura de dados de uma rvore genealgica.

Binding
O mtodo

bind

, est presente em todas as funes, ele cria uma nova funo que chama a funo original mas

com alguns argumentos j fixados.


O cdigo a seguir mostra um exemplo de uso do
pessoa est em um determinado conjunto de

bind

string

. Ele define uma funo

. Ao chamar

filter

isInSet

, que nos diz se uma

a fim de selecionar os objetos pessoa

cujos nomes esto em um conjunto especfico. Ns podemos escrever uma expresso de funo que faz a

72

chamada para
isInSet

enviando nosso conjunto como primeiro argumento ou parcialmente aplicar a funo

isInSet

var theSet = ["Carel Haverbeke", "Maria van Brussel",


"Donald Duck"];
function isInSet(set, person) {
return [Link]([Link]) > -1;
}
[Link]([Link](function(person) {
return isInSet(theSet, person);
}));
// [{name: "Maria van Brussel", },
//

{name: "Carel Haverbeke", }]

[Link]([Link]([Link](null, theSet)));
// same result

A chamada usando

bind

retorna uma funo que chama

isInSet

com

theset

sendo o primeiro argumento,

seguido por todos os demais argumentos indicados pela funo vinculada.


O primeiro argumento onde o exemplo passa
primeiro argumento do

apply

null

, utilizado para as chamadas de mtodo, semelhante ao

. Eu vou descrever isso com mais detalhes no prximo captulo.

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

fornece uma grande quantidade de funes de ordem superior -

array

filter

para construir um novo

array

com valores filtrados,

cada elemento colocado atravs de uma funo e

reduce

forEach

map

faz algo com cada elemento de

para construir um novo array onde

para combinar todos os elementos de um

array

em

um valor nico.
Funes tm o mtodo

apply

que pode ser usado para chamar um

tambm possuem um mtodo

bind

array

especificando seus argumentos. Elas

que usado para criar uma verso parcial da funo que foi aplicada.

Exerccios
Juntando
Use o mtodo

reduce

juntamente com o mtodo

que tem todos os elementos de entrada do

array

concat

para juntar um

array

de

arrays

em um nico

array

var arrays = [[1, 2, 3], [4, 5], [6]];


// Your code here.
// [1, 2, 3, 4, 5, 6]

Diferena de idade entre me e filho


Usando os dados de exemplo definidos neste captulo, calcule a diferena de idade mdia entre mes e filhos (a
idade da me quando a criana nasce). Voc pode usar a funo

73

average

definida anteriormente neste captulo.

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 ascendncia produz dados teis (no podemos calcular a diferena

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

para a primeira filtragem. Alternativamente voc pode comear a chamar o

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

O Histrico da expectativa de vida


Quando olhamos para todas as pessoas no nosso conjunto de dados que viveram mais de 90 anos, s a ltima
gerao dos dados que retornou. Vamos observar mais de perto esse fenmeno.
Calcule o resultado da idade mdia das pessoas no conjunto de dados definidos por sculo. Uma pessoa
atribuda a um sculo pegando o ano da sua morte, dividindo por 100 e arredondando para cima com
[Link]([Link] / 100)

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

Para ganhar um ponto extra escreva uma funo


aceitar um

array

groupBy

que abstrai a operao de agrupamento. Ele deve

como argumento e uma funo que calcula cada elemento do grupo de

objeto que mapeia os nomes dos grupos de

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

com os ancestrais de cada sculo.

74

Durante o processo de agrupamento, mantenha um objeto que associa os nomes dos sculos (nmeros) com
os

de objetos de pessoas ou idades. J que no sabemos quais agrupamentos iremos encontrar,

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

para ele. Em seguida adicione a pessoa (ou idade) no

array

array

de acordo com o sculo apropriado.


Finalmente um

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

predicada que quando chamada com um


operador
retorna

&&

true

predicada

retorna apenas

true

array

como argumento retorna


true

array

retorna quando algum elemento do

array

tiver um valor como


some

ou

false

. Assim como o

array
true

true

every

. Sendo assim, a funo


. Ele no processa mais

encontrar o que precisa no primeiro elemento

ele no percorrer os outros elementos.

Escreva duas funes, que se comporte como esses mtodos,


array

true

para cada elemento do

elementos do que o necessrio - por exemplo, se o predicado


do

(alguns). Ambos recebem uma funo

some

como valor quando as expresses de ambos os lados forem

quando a funo predicada retorna

some

(todos) e

every

every

some

, exceto se eles receberem um

como seu primeiro argumento ao invs de um mtodo.

// Your code here.


[Link](every([NaN, NaN, NaN], isNaN));
// true
[Link](every([NaN, NaN, 4], isNaN));
// false
[Link](some([NaN, 3, 4], isNaN));
// true
[Link](some([2, 3, 4], isNaN));
// false

Dica:
As funes podem seguir um padro semelhante definio de

forEach

que foi mostrado no incio do captulo, a

nica exceo que eles devem retornar imediatamente (com o valor direita) quando a funo predicada retorna
true

ou

false

. No se esquea de colocar uma outra instruo de

retorne um valor correto quando atingir o final do

array

75

return

aps o

loop

; para que a funo

A vida secreta dos objetos


"O problema com as linguagens orientadas a objeto que elas tm tudo implcito no ambiente que elas
carregam consigo. Voc queria banana, mas o que voc teve foi um gorila segurando a banana e toda a
floresta." `Joe Armstrong, entrevistado em Coders at Work``
Quando um programador diz "objeto", isso um termo carregado. Na minha profisso, objetos so a maneira de
viver, o sujeito das guerras santas, e um jargo apaixonante que ainda no perdeu o seu poder.
Para um estrangeiro, isso provavelmente um pouco confuso. Vamos comear com uma rpida histria dos
objetos como constrtutores da programao.

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
//

late it's getting!'

[Link]("I could sure use a carrot right now.");


// The fat rabbit says 'I could sure use a carrot
//

right now.'

O cdigo acima usa a palavra chava


ambos os mtodos

apply

bind

this

para dar a sada do tipo de coelho que est falando. Lembrando que

podem usar o primeiro argumento para simular chamadas de mtodos. Esse

primeiro argumento, na verdade, usado para passar um valor ao


Existe um mtodo parecido ao

apply

chamado

call

this

. Ele tambm chama a funo na qual ele um mtodo e

aceita argumentos normalmente, ao invs de um array. Assim como


com um valor especfico no

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]

Eu acabei de sacar uma propriedade de um objeto vazio. Mgica!

78

apply

bind

,o

call

pode ser passado

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]

retorna o prototype de um objeto como o esperado.

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

toString, que converte um objeto para uma representao em string.


Muitos objetos no possuem o

[Link]

diretamente em seu prototype. Ao invs disso eles tm outro

objeto que fornece suas propriedades padro. Funes derivam do


[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

obviamente retornaro o prototype de um objeto. Voc pode usar

[Link]

para

criar um objeto com um prototype especfico.


var protoCoelho = {
fala: function(linha) {
[Link]("O coelho " + [Link] + " fala '" +
linha + "'");
}
};
var coelhoAssassino = [Link](protoCoelho);
[Link] = "assassino";
[Link]("SKREEEE!");
// O coelho assassino fala 'SKREEEE!'

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

vai fazer com que ela seja tratada como um

atrelada a um objeto novo, e a menos que ele explicite o retorno do

valor de outro objeto, esse novo objeto ser retornado a partir da chamada.
Um objeto criado com

new

chamado de instncia do construtor.


79

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

Construtores (todas as funes, na verdade) pegam automaticamente uma propriedade chamada


por padro possui um objeto vazio que deriva do

[Link]

Coelho

, que

. Toda instncia criada com esse construtor ter

esse objeto assim como seu prototype. Ento, para adicionar um mtodo
construtor

prototype

fala

aos coelhos criados com o

, ns podemos simplesmente fazer isso:

[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]

). O prototype propriamente dito de um construtor

construtores so funes. Sua propriedade

prototype

[Link]

, visto que os

ser o prototype de instncias criadas atravs dele mas

no ser seu prprio prototype.

Definindo uma tabela


Eu vou trabalhar sobre um exemplo ou pouco mais envolvido na tentativa de dar a voc uma melhor ideia de
polimorfismo, assim como de programao orientada a objetos em geral. O projeto este: ns vamos escrever
um programa que, dado um array de arrays de clulas de uma tabela, cria uma string que contm uma tabela
bem formatada - significando que colunas so retas e linhas esto alinhadas. Algo dessa forma:
name

height country

------------ ------ ------------Kilimanjaro

5895 Tanzania

Everest

8848 Nepal

Mount Fuji

3776 Japan

Mont Blanc

4808 Italy/France

Vaalserberg

323 Netherlands

Denali

6168 United States

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)

retorna um array de tamanho

height

, que contm uma srie de strings que contm

width

caracteres de tamanho. Isso representa o contedo da clula.


Irei fazer forte uso de mtodos de ordem superior de array neste exemplo uma vez que isso apropriado para
essa abordagem.
A primeira parte do programa calcula matrizes de largura e altura mnima para uma grade de clulas.
function rowHeights(rows) {
return [Link](function(row) {
return [Link](function(max, cell) {
return [Link](max, [Link]());
}, 0);
});
}
function colWidths(rows) {
return rows[0].map(function(_, i) {
return [Link](function(max, row) {
return [Link](max, row[i].minWidth());
}, 0);
});
}

Usar um nome de varivel que se inicia com um underscore (

) ou consistir inteiramente em um simples

underscore uma forma de indicar (para leitores humanos) que este argumento no ser usado.
A funo

rowHeights

no deve ser to difcil de ser seguida. Ela usa

array de clulas e envolve isso em um

map

As coisas so um pouco mais difceis na funo


colunas. No mencionei at agora que

map

reduce

para computar a altura mxima de um

para fazer isso em todas as linhas no array


colWidths

(assim como

rows

porque o array externo um array de linhas, no de


forEach

filter

e mtodos de array similares) passam

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

pega a largura da clula mais larga nesse ndice.


Aqui est o cdigo para desenhar a tabela:

81

colWidths

roda sobre o array

constri um array com um

rows

exterior para cada ndice e

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

usa a funo interna

drawRow

para desenhar todas as linhas e ento as junta com caracteres

newline (nova linha).


A funo

drawRow

primeiro converte os objetos clula na linha em b locos, que so arrays representando o

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"]

duas linhas e ser representada pelo array

["name", "----"]

, onde uma clula sublinhada deve conter

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

constri essa sada linha por linha mapeando sobre as linhas do

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

em cada ocorrncia do seu argumento e retorna um array com as partes. O mtodo

minWidth

com maior largura nesse array.

82

, que corta uma string


encontra a linha

function repeat(string, times) {


var result = "";
for (var i = 0; i < times; i++)
result += string;
return result;
}
function TextCell(text) {
[Link] = [Link]("\n");
}
[Link] = function() {
return [Link](function(width, line) {
return [Link](width, [Link]);
}, 0);
};
[Link] = function() {
return [Link];
};
[Link] = function(width, height) {
var result = [];
for (var i = 0; i < height; i++) {
var line = [Link][i] || "";
[Link](line + repeat(" ", width - [Link]));
}
return result;
};

O cdigo usa uma funo auxiliar chamada


string

repetido

times

repeat

, que constri uma linha na qual o valor um argumento

nmero de vezes. O mtodo

draw

usa isso e adiciona "preenchimento" para as linhas

assim todas vo ter o tamanho requerido.


Vamos testar tudo que construmos e criar um tabuleiro de damas 5 x 5.
var rows = [];
for (var i = 0; i < 5; i++) {
var row = [];
for (var j = 0; j < 5; j++) {
if ((j + i) % 2 == 0)
[Link](new TextCell("##"));
else
[Link](new TextCell("

"));

}
[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

na sandb ox e tambm neste arquivo em nosso website.

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

desta clula) mas adicionando um largura para contar o

espao usado pelo sublinhado.


Desenhar essa clula bem simples - ns pegamos o contedo da clula interna e concatenamos uma simples
linha preenchida com traos a ela.
Tendo um mecanismo de sublinhamento, ns podemos agora escrever uma funo que constri uma grade de
clulas a partir do conjunto de dados.
function dataTable(data) {
var keys = [Link](data[0]);
var headers = [Link](function(name) {
return new UnderlinedCell(new TextCell(name));
});
var body = [Link](function(row) {
return [Link](function(name) {
return new TextCell(String(row[name]));
});
});
return [headers].concat(body);
}
[Link](drawTable(dataTable(MOUNTAINS)));
// name

height country

//

------------ ------ -------------

//

Kilimanjaro

//

etcetera

A funo padro

5895

Tanzania

[Link]

retorna um array com nomes de propriedades de um objeto. A linha do topo da tabela

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

. Vamos chegar nessa parte em um instante.

Getters and Setters


Quando especificamos uma interface, possvel incluir propriedades que no so mtodos. Poderamos ter
definido

minHeight

minWidth

para simplesmente conter nmeros. Mas isso teria exigido de ns comput-los no

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

escrevendo - e lendo - muitos mtodos adicionais.


Felizmente, o JavaScript fornece uma tcnica que fornece o melhor de ambos os mundos. Ns podemos
especificar propriedades que, do lado de fora, parecem propriedades normais mas secretamente tem mtodos
associados a elas.
var pile = {
elements: ["eggshell", "orange peel", "worm"],
get height() {
return [Link];
},
set height(value) {
[Link]("Ignoring attempt to set height to", value);
}
};
[Link]([Link]);
// 3
[Link] = 100;
// Ignoring attempt to set height to 100

Em um objeto literal, a notao

get

ou

set

para propriedades permite que voc especifique uma funo a ser

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]

(que ns previamente usamos para

criar propriedades no enumerveis).


[Link]([Link], "heightProp", {
get: function() { return [Link]; }
});
var cell = new TextCell("no\nway");
[Link]([Link]);
// 2
[Link] = 100;
[Link]([Link]);
// 2

Voc pode usar a propriedade similar

set

, no objeto passado

defineProperty

, para especificar um mtodo

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

, mas ao invs de dar

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;
};

Ns reusamos o construtor e os mtodos


equivalente a

TextCell

minHeight

, exceto que seu mtodo

draw

minWidth

de

TextCell

. Um

RTextCell

agora basicamente

contm uma funo diferente.

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

para as clulas cujo valor um

nmero, vamos obter a tabela que estvamos buscando.


function dataTable(data) {
var keys = [Link](data[0]);
var headers = [Link](function(name) {
return new UnderlinedCell(new TextCell(name));
});
var body = [Link](function(row) {
return [Link](function(name) {
var value = row[name];
// This was changed:
if (typeof value == "number")
return new RTextCell(String(value));
else
return new TextCell(String(value));
});
});
return [headers].concat(body);
}
[Link](drawTable(dataTable(MOUNTAINS)));
// beautifully aligned table

Herana uma parte fundamental da orientao a objetos tradicional, ao lado de encapsulamento e


polimorfismo. Mas enquanto os dois ltimos sejam agora geralmente considerados como ideias brilhantes,
herana algo controverso.
A principal razo para isso que este tpico geralmente confundido com polimorfismo, vendido como uma
ferramenta mais poderosa do que realmente , e subsequentemente usado em excesso de diversas horrveis
formas. Onde encapsulamento e polimorfismo podem ser usados para separar pedaos de cdigo de cada um,
reduzindo o emaranhamento de todo o programa, herana fundamentalmente vincula os tipos, criando mais
emaranhados.

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

constri em outra clula simplesmente armazenando-a em uma propriedade e um mtodo

posterior a chama nos seus prprios mtodos.

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

[Link](new RTextCell("A") instanceof RTextCell);


// true
[Link](new RTextCell("A") instanceof TextCell);
// true
[Link](new TextCell("A") instanceof RTextCell);
// false
[Link]([1] instanceof Array);
// true

O operador vai olhar atravs dos tipos herdados. Um


[Link]
Array

deriva de

[Link]

RTextCell

uma instncia de

TextCell

porque

. O operador pode ser aplicado a construtores padro como

. Praticamente todos os objetos so uma instncia de

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]

como seus prottipos.

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

de um tipo compartilham em seus prottipos. O operador

instanceof

pode, dado um objeto e um construtor, dizer

se o objeto uma instncia deste construtor.


Algo til a se fazer com objetos especificar uma interface para eles e dizer para todos quer iro supostamente
conversar com seu objeto a fazer isso somente por essa interface. O resto dos detalhes que constroem seu
objeto esto agora encapsulados, escondidos atrs da interface.
Uma vez que voc esteja conversando em termos de interfaces, quem diz que apenas um tipo de objeto pode
implementar essa interface? Ter diferentes objetos expondo a mesma interface chamado de polimorfismo. Isso
muito til.
Quando implementando vrios tipos que diferem apenas em alguns detalhes, pode ser til simplesmente criar o
prottipo do seu novo tipo derivando do prottipo do seu antigo tipo e ter seu novo construtor chamando o antigo.
Isso lhe d um tipo similar de objeto ao antigo mas que permite que voc adicione ou sobrescreva propriedades
quando necessrio.

Exerccios
87

Um tipo de vetor
Escreva um construtor
parmetros

D ao prottipo de

Vector

que represente um vetor em duas dimenses do espao. Ele recebe os

(nmeros), que deve salvar em propriedades de mesmo nome.


Vector

dois mtodos,

plus

minus

novo vetor que tem a soma ou diferena dos valores

, que pegam outro vetor como parmetro e retornam um


e

dos dois vetores (o vetor que est em

this

eo

passado no parmetro).
Adicione uma propriedade getter
(

x, y

length

ao prottipo que calcula o tamanho do vetor - isto , a distncia do ponto

) at a origem (0,0).

// Your code here.


[Link](new Vector(1, 2).plus(new Vector(2, 3)));
// Vector{x: 3, y: 5}
[Link](new Vector(1, 2).minus(new Vector(2, 3)));
// Vector{x: -1, y: -1}
[Link](new Vector(3, 4).length);
// 5

Dicas
Sua soluo pode seguir o padro do construtor

Rabbit

deste captulo de forma bem semelhante.

Adicionar uma propriedade getter ao construtor pode ser feita com a funo
distncia do ponto

(0, 0)

at

(x, y)

[Link]

. Para calcular a

voc pode usar o teorema de Pitgoras, que diz que o quadrado da

distncia que estamos procurando igual ao quadrado da coordenada x mais o quadrado da coordenada y.
Assim,

o nmero que voc quer, e

(x2 + y2)

[Link]

o caminho para voc calcular a raiz quadrada no

JavaScript.

Outra clula
Implemente uma clula do tipo

StretchCell(inner, width, height)

que se adeque a interface da clula da tabela

descrita anteriormente neste captulo. Ela deve envolver outra clula (como
clula resultante tem pelo menos a largura (

width

) e altura (

height

faz) e assegurar que a

UnderlinedCell

) especificada, mesmo se a clula interior for

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

devem chamar atravs dos mtodos correspondentes na clula interna (

nenhum nmero menor que o tamanho dado retornado (possivelmente usando


No se esquea de adicionar um mtodo

draw

inner

minWidth

), mas assegure-se que

[Link]

).

que simplesmente encaminha a chamada para a clula interior.

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

que pega um objeto sequencial

para seus primeiros 5 elementos - ou menos, se a sequncia tiver menos do que cinco

elementos.
Ento implemente um tipo de objeto

ArraySeq

que envolve um array e permite interao sobre o array usando a

interface que voc desenvolveu. Implemente outro tipo de objeto


(recebendo os argumentos

from

to

RangeSeq

que itera sobre um intervalo de inteiros

em seu construtor).

// Your code here.


logFive(new ArraySeq([1, 2]));
// 1
// 2
logFive(new RangeSeq(100, 1000));
// 100
// 101
// 102
// 103
// 104

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

Projeto - Vida eletrnica


[...] A questo da mquinas poder pensar [...] to relevante quanto a questo dos submarinos poderem
nadar.
Edsger Dijkstra, The Threats to Computing Science
Nos captulo "Projeto" irei apresentar uma nova teoria por um breve momento e trabalhar atravs de um programa
com voc. A teoria indispensvel quando estamos aprendendo a programar mas deve ser acompanhada da
leitura para entender os programas no triviais.
Nosso projeto neste captulo construir um ecossistema virtual, um mundo pequeno povoado com criaturas que
se movem e luta pela sobrevivncia.

Definio
Para tornar esta tarefa gerencivel, vamos simplificar radicalmente o conceito de um mundo. Ou seja, um mundo
ser uma

bidimensional onde cada entidade ocupa um quadrado da

grid

grid

. Em cada turno os bichos todos

tm a chance de tomar alguma ao.


Utilizaremos o tempo e o espao com um tamanho fixo como unidades. Os quadrados sero os espaos e as
voltas o tempo. claro que as aproximaes sero imprecisas. Mas nossa simulao pretende ser divertida para
que possamos livremente cortar as sobras.
Podemos definir um mundo com uma matriz de

Strings

que estabelece uma

grid

do mundo usando um

personagem por metro quadrado.


var plan = ["############################",
"#

##",

"#

#",

"#

#####

"##

"###
"#

##

#",

##

#",

#",

###

"#

####

"#

##

"# o

"#

#",

#",
o

#",
o

### #",
#",

"############################"];

Os caracteres

"#"

representam as paredes e rochas, e os personagens

"O"

representam os bichos. Os

espaos como voc j deve ter pensado o espao vazio.


Um plano de matriz pode ser usada para criar um objeto de mundo. Tal objeto mantm o controle do tamanho e
do contedo do mundo. Ele tem um mtodo

toString

, que converte o mundo de volta para uma sequncia de

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);
};

Em seguida, temos um tipo de objeto que o modelo da

grid

.A

grid

uma parte do mundo, e tornamos ela um

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

deve preocupar-se com as coisas

Para armazenar um valor a

grid

temos vrias opes. Podemos usar um

array

de

arrays

tendo duas

propriedades de acessos para chegar a um quadrado especfico como este:


var grid = [["top left",

"top middle",

"top right"],

["bottom left", "bottom middle", "bottom right"]];


[Link](grid[1][2]);
// bottom 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.

var grid = ["top left",

"top middle",

"top right",

"bottom left", "bottom middle", "bottom right"];


[Link](grid[2 + (1 * 3)]);
// bottom 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

comprimento que foi passado de parmetro.


Esse cdigo define o objeto

grid

, com alguns mtodos bsicos:

function Grid(width, height) {


[Link] = new Array(width * height);
[Link] = width;
[Link] = height;
}
[Link] = function(vector) {
return vector.x >= 0 && vector.x < [Link] &&
vector.y >= 0 && vector.y < [Link];
};
[Link] = function(vector) {
return [Link][vector.x + [Link] * vector.y];
};
[Link] = function(vector, value) {
[Link][vector.x + [Link] * vector.y] = value;
};

Aqui esta um exemplo trivial do teste:

91

var grid = new Grid(5, 5);


[Link]([Link](new Vector(1, 1)));
// undefined
[Link](new Vector(1, 1), "X");
[Link]([Link](new Vector(1, 1)));
// X

A interface da programao dos bichos


Antes de comearmos nosso construtor global, devemos especificar quais os objetos bichos que estaro vivendo
em nosso mundo. Eu mencionei que o mundo vai especificar os bichos e as aes que eles tero. Isso funciona
da seguinte forma: cada bicho um objeto e tem um mtodo de ao que quando chamado retorna uma ao.
Uma ao um objeto com uma propriedade de tipo, que d nome a ao que o bicho ter, por exemplo

"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

. Mas essa viso limitada

chamado o objeto de verificao permite que o

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

direes para coordenar os

offsets

var directions = {
"n":

new Vector( 0, -1),

"ne": new Vector( 1, -1),


"e":

new Vector( 1,

0),

"se": new Vector( 1,

1),

"s":

new Vector( 0,

1),

"sw": new Vector(-1,

1),

"w":

0),

new Vector(-1,

"nw": new Vector(-1, -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

. Ambos tomam um mapa de carter como um argumento. O primeiro retorna

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

contendo todas as direes possveis para o personagem. Por exemplo,

uma criatura sentada esquerda(oeste) de um muro vai ter ["ne", "e", "se"] ao chamar

findAll

passando o

caractere "#" como argumento.


Aqui um bicho simples e estpido que segue apenas seu nariz at que ela atinja um obstculo e depois salta
para fora em uma direo aleatria:

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

simplesmente pega um elemento aleatrio de uma matriz usando

[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

nomes de direo. Ns tambm poderamos termos usado

chama

[Link]

randomElement

em uma matriz de

para obter essa matriz de direes que

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"

no mtodo de ao serve para impedir que

[Link]

obtenha um valor nulo para o bicho que

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

sendo este o valor que vamos usar para representar o

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

um novo para ele. Em seguida adicionado uma propriedade

originChar

tornando mais fcil de descobrir em

qual personagem o elemento foi originalmente criado.


Precisamos da propriedade

originChar

quando implementarmos o mtodo

toString

no mundo. Este mtodo

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.

var world = new World(plan, {"#": Wall,


"o": BouncingCritter});
[Link]([Link]());
// ############################
//

//

//

#####

//

##

//

###

//

//

####

//

##

//

# o

//

//

############################

This

##
#
#

##

##

###

#
o

#
o

### #
#

e seu escopo

O construtor do mundo contm uma chamada de


dentro da funo do

forEach

forEach

. Uma coisa interessante que podemos notar que

no estamos mais no escopo da funo do construtor. Cada chamada de funo

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

para acessar nada de fora de dentro do

, onde a funo interna tera acesso a ela.


94

loop

. Podemos criar

Isso um erro de

design

no JavaScript. Felizmente a prxima verso da linguagem ir fornecer uma soluo para

este problema. Enquanto isso existem solues alternativas. Um padro comum dizer

var auto = this

uma

varivel local que guarda sua referencia.


Outra soluo usar o mtodo de

bind

que nos permite oferecer uma chamada explcita para o objeto.

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

que esta dentro do objeto

test

somado ao resultado do

A maioria dos mtodos que mapeiam matrizes tais como

forEach

map

, tm um segundo argumento opcional

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

para o nosso tipo de

grid

, chamaremos uma

que no seja nulo ou indefinido:

[Link] = function(f, context) {


for (var y = 0; y < [Link]; y++) {
for (var x = 0; x < [Link]; x++) {
var value = [Link][x + y * [Link]];
if (value != null)
[Link](context, value, new Vector(x, y));
}
}
};

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

que acabamos de definir a procura de objetos com um

. Quando ele encontra um ele chama o mtodo para obter uma ao e realiza a ao quando ela for

vlida. Por enquanto apenas as aes

"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);
};

Ns usamos o contexto como segundo parmetro no mtodo

forEach

conseguirmos acessar corretamente as funes internas. O mtodo

para ser a referncia da


letAct

grid

para

contm a lgica real que permite que

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"

refere a um sentido vlido caso o


quadrado

ele ser ignorado. Se


quadrado

"move"

de destino e ao se mover novamente vamos definir

bicho na prximo
Perceba que

quadrado

letAct

ele ter uma propriedade de direo que se

na direo referida estiver vazio(


null

null

). Iremos definir o bicho para o

para este quadrado visitado e armazenar o

no ignora coisas que no fazem sentidos como, se a propriedade direo vlida ou se a

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(

). Isso far com que os

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);
};

O mtodo observa e descobre se as coordenadas que estamos visitando est dentro da


personagem correspondente ao elemento. Para coordenadas fora da

grid

grid

e se o

podemos simplesmente fingir que h

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

oferece uma funo

animateWorld

segundo at que voc aperte o boto de

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

que discutem a integrao JavaScript em navegadores web, ele no sera to mgico.

Mais formas de vida


97

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

, precisamos definir nossa prpria operao(

dirPlus

) para calcular as direes relativas. Ento

significa 45 graus no sentido horrio para norte quando retornar "ne". Da mesma forma

dirPlus("n", 1)
dirPlus("s", -2)

significa 90 graus para a esquerda ao sul retornando leste.

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"

os arredores do bicho a partir do seu lado esquerdo no sentido horrio at encontrar

um quadrado vazio. Em seguida ele se move na direo do quadrado vazia.


O que complica que um bicho pode acabar no meio de um espao vazio, quer como a sua posio de partida ou
como um resultado de caminhar em torno de um outro bicho. Se aplicarmos a abordagem que acabei de
descrever no espao vazio o bicho vai apenas virar esquerda a cada passo correndo em crculos.
Portanto, h uma verificao extra(instruo

if

) no inicio da digitalizao para a esquerda para analisar se o

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]

para comear aps cada passagem do lao para se certificar de

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

Uma simulao mais realista


Para tornar a vida em nosso mundo mais interessante vamos adicionar os conceitos de alimentao e
reproduo. Cada coisa viva no mundo ganha uma nova propriedade, a energia, a qual reduzida por realizar
aes e aumenta comendo alguma coisas. Quando o bicho tem energia suficiente ele pode se reproduzir,
gerando um novo bicho da mesma espcie. Para manter as coisas simples; os bichos em nosso mundo se
reproduzem assexuadamente ou seja por si so.
Se bichos s se movimentar e comer uns aos outros o mundo em breve ira se sucumbir na lei da entropia
crescente, ficando sem energia e tornando um deserto sem vida. Para evitar que isso acontea(muito
rapidamente pelo menos) adicionaremos plantas para o mundo. As plantas no se movem. Eles s usam a
fotossntese para crescer(ou seja aumentar a sua energia) e se reproduzir.
Para fazer este trabalho vamos precisar de um mundo com um mtodo diferente de

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

deve executar uma ao para vrias funes armazenados no objeto

, cujo seu prottipo baseado no

letAct

delega o trabalho do que realmente

actionTypes

function LifelikeWorld(map, legend) {


[Link](this, map, legend);
}
[Link] = [Link]([Link]);
var actionTypes = [Link](null);
[Link] = function(critter, vector) {
var action = [Link](new View(this, vector));
var handled = action &&
[Link] in actionTypes &&
actionTypes[[Link]].call(this, critter,
vector, action);
if (!handled) {
[Link] -= 0.2;
if ([Link] <= 0)
[Link](vector, null);
}
};

O novo mtodo

letAct

verifica primeiro se uma ao foi devolvido, ento se a funo manipuladora para este tipo

de ao existir, o resultado deste manipulador sera

true

, indicando que ele tratou com sucesso a ao. Observe

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"

e sera usado pelas plantas. Quando um objeto

devolvido o seguinte mtodo de manipulao ser chamado:

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;
};

Esta ao verifica primeiro se o destino vlido usando o mtodo

checkDestination

no est vazio ou se o bicho no tem energia necessria; o movimento retorna

. Se no vlido, se o destino

false

para indicar que nenhuma

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

E finalmente ns permitiremos que os nossos bichos se reproduzem.


[Link] = function(critter, vector, action) {
var baby = elementFromChar([Link],
[Link]);
var dest = [Link](action, vector);
if (dest == null ||
[Link] <= 2 * [Link] ||
[Link](dest) != null)
return false;
[Link] -= 2 * [Link];
[Link](dest, baby);
return true;
};

Reproduzir custa duas vezes mais o nvel de energia de um bicho recm-nascido. Ento primeiro criamos o beb
(hipoteticamente) usando

elementFromChar

no prprio carter origem do bicho. Uma vez que temos um beb

podemos encontrar o seu nvel de energia e testar se o pai tem energia suficiente para traz-lo com sucesso no
100

mundo. Tambm exigido um destino vlido(vazio).


Se tudo estiver bem o beb colocado sobre a

grid

(que j no hipoteticamente), e a energia subtrada do

pai.

Populando o novo mundo


Agora temos um quadro para simular essas criaturas mais realistas. Poderamos colocar os bichos do velho
mundo para o novo, mas eles s iriam morrer, uma vez que no temos uma propriedade de energia. Ento vamos
fazer novos elementos. Primeiro vamos escrever uma planta que uma forma de vida bastante simples.
function Plant() {
[Link] = 3 + [Link]() * 4;
}
[Link] = function(context) {
if ([Link] > 15) {
var space = [Link](" ");
if (space)
return {type: "reproduce", direction: space};
}
if ([Link] < 20)
return {type: "grow"};
};

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