Skip to content

Instantly share code, notes, and snippets.

@baltazarparra
Created August 26, 2016 18:55
Show Gist options
  • Save baltazarparra/59d93880602d8c352ef4fd93cf62d1ff to your computer and use it in GitHub Desktop.
Save baltazarparra/59d93880602d8c352ef4fd93cf62d1ff to your computer and use it in GitHub Desktop.

You Don't Know JS: Iniciando

Introdução

Qual foi a última vez que você aprendeu algo novo?

Talvez tenha sido um novo idioma, como Italiano ou Alemão. Ou talvez um editor de imagens, como Photoshop. Ou alguma técnica culinária ou carpintaria ou uma rotina de exercícios. Eu gostaria de te lembrar aquele sentimento que existe quando você finalmente chega lá: o momento "a-ha". Quando as coisas deixam de ser escuras e turvas e ficam claras como o dia, como dominar a serra de mesa ou entender a diferença entre os pronomes masculinos e femininos em Francês. Como você se sentiu? Foi maravilhoso, não foi?

Agora gostaria que você voltasse um pouco no tempo para aquele momento antes de você aprender sua nova habilidade. Como você se sentiu? Provavelmente um pouco intimidado e talvez até frustrado. Em certo ponto, não sabíamos as coisas que sabemos hoje e não tem problema nenhum nisso. Todo mundo começa de algum lugar. Aprender um novo material é uma aventura empolgante, principalmente se você está efetivamente disposto a aprender um tópico.

Eu ministro diversas aulas sobre programação para iniciantes. Os estudantes que assistem minhas aulas muitas vezes já tentaram aprender sozinhos HTML ou JavaScript, lendo blogs ou copiando e colando códigos, mas nenhum deles foi capaz de realmente dominar a matéria a ponto de habilitá-los a ter o resultado que esperam. Por não conseguirem entender o sentido real de certos assuntos, eles não conseguem escrever algo relevante ou debugar seu próprio trabalho, por não entenderem realmente o que está acontecendo.

Eu sempre me dediquei a ensinar do jeito certo. Quero dizer que ensino meus alunos a respeitar os padrões da Web, semântica, comentar bem seus códigos e outras boas práticas. Eu cubro o assunto de forma exaustiva, para explicar o como e o porquê, sem simplesmente cuspir códigos para serem copiados e colados. Quando você se esforça para entender seu código, você cria um trabalho melhor e se torna melhor no que faz. Aquele código não é mais apenas seu trabalho, ele é sua criação. Esse é o motivo de eu curtir tanto Iniciando. O Kyle nos faz nos aprofundar na sintaxe e terminologia para nos dar uma grande introdução ao JavaScript, sem utilizar nenhum atalho. Este livro não é nem um pouco superficial e nos permite, genuinamente, entender os conceitos que escreveremos.

Não é suficiente saber duplicar snippets jQuery em seu site, assim como não é suficiente aprender como abrir, fechar e salvar documentos no Photoshop. É claro, uma vez aprendido o básico do programa eu fui capaz de criar e compartilhar um design que fiz, mas sem, legitimamente, conhecer as ferramentas e o que elas são capazes, como eu poderia definir um grid, ou definir um sistema de tipografia legível, ou otimizar gráficos para web. O mesmo acontece com JavaScript. Sem saber como os loops funcionam, ou como definir variáveis, ou o que é um escopo, não estaremos escrevendo o melhor código que poderíamos. E não desejamos fazer nada menos que isso - essa é, afinal de contas, nossa criação.

Quanto mais exposto ao JavaScript você está, mais claro ele se torna. Palavras como clausura, objeto e método podem parecer fora da sua zona de conforto agora, mas este livro irá ajudá-lo a clarear sua mente sobre esses termos. Eu gostaria que você marcasse esses dois sentimentos - o antes e o depois - que você aprende algo novo, antes de começar este livro. Pode parecer assustador agora, mas você escolheu este livro porque está iniciando uma incrível jornada para refinar seu conhecimento. Iniciando é o início da caminhada para entender sobre programação. Curta seus momentos a-ha!

You Don't Know JS

Prefácio

Tenho certeza que você notou, mas o "JS" como título da série não é uma abreviação de palavras usadas para falar mal de JavaScript, embora xingar sobre as peculiaridades da linguagem é algo com o qual todos nós podemos provavelmente nos identificar.

Desde os primórdios da web, o JavaScript tem sido a fundação de tecnologias que determinam a experiência interativa do conteúdo que consumimos. Apesar de efeitos nos traçados do mouse e perturbadores pop-ups com prompts terem sido a forma que o JavaScript começou, quase duas décadas depois, as capacidades do JavaScript cresceram em muitas ordens de magnitude, e poucos duvidam de sua importância como coração da plataforma de software mais acessível do mundo: a web.

Como linguagem, tem sido alvo permanente de críticas, em parte por sua herança mas principalmente pela filosofia de sua concepção. Até o seu nome evoca, como Brendan Eich uma vez colocou, um status de "irmão mais novo bobinho" se comparado ao seu irmão mais velho e maduro "Java". De qualquer forma, o nome é um mero acidente relacionado à políticas e marketing. As duas linguagens são bem diferentes em muitos pontos importantes. "JavaScript" está tão relacionado a "Java" quanto "Casaco" está relacionado com "Casa".

Pelo fato do JavaScript adotar conceitos e sintaxe idiomática de diversas linguagens, incluindo orgulhosas raízes procedurais ao estilo de C, assim como sutis e menos óbvias raízes funcionais ao estilo de Scheme/Lisp, é bastante acessível para uma larga audiência de desenvolvedores(as), até mesmo aqueles com pouca ou nenhuma experiência com programação. O exemplo de "Hello World" do JavaScript é tão simples que a linguagem é convidativa e fácil de se sentir confortável logo no primeiro contato.

Apesar do JavaScript ser talvez uma das linguagens mais fáceis de se começar, suas excentricidades fazem com que o domínio sólido da linguagem seja muito menos comum do que em outras linguagens. Enquanto se precisa um conhecimento aprofundado em linguagens como C ou C++ para escrever um programa em grande escala, uma aplicação similar em JavaScript pode ser desenvolvida, e muitas vezes é, apenas arranhando parte do que a linguagem pode fazer de verdade.

Conceitos sofisticados e profundamente enraizados na linguagem tendem a surgir de modo aparentemente simples, como a utilização de funções como callbacks, o que encoraja o(a) desenvolvedor(a) JavaScript a usar a linguagem apenas como forma de alcançar um objetivo, sem se preocupar muito com o que está acontecendo nos bastidores.

É, simultaneamente, uma linguagem simples, fácil de usar e com forte aceitação, mas também um conjunto de mecanismos de linguagem complexos e cheios de nuances, cuja falta de um estudo minucioso pode mascarar um verdadeiro entendimento até mesmo para os(as) desenvolvedores(as) JavaScript mais experientes.

É aí que se encontra o paradoxo do JavaSript, o tendão de Aquiles da linguagem, o desafio que estamos propondo. Pelo fato do JavaScript poder ser utilizado sem sua compreensão, o verdadeiro conhecimento sobre a linguagem geralmente nunca é alcançado.

Missão

Se a cada ponto que você encontrar uma surpresa ou se sentir frustrado com JavaScript, sua resposta for adicionar mais um item à sua lista negra, como muitos(as) estão acostumados(as) a fazer, você em breve entrará em uma penumbra cinzenta que irá te afastar de toda a riqueza da linguagem.

A ideia de aprender JavaScript separando todas as partes frustrantes da linguagem foi famosamente apelidada de "A parte boa". Eu vou implorar a você, querido(a) leitor(a), para ao invés disso, considerar essa parte como "A Parte Fácil" ou "A Parte Segura", ou até mesmo "A Parte Incompleta".

A série de livros You Don't Know JavaScript oferece um desafio inverso: aprender e entender profundamente tudo sobre JavaScript, até mesmo "As Partes Difíceis".

Aqui, nós abordamos profundamente a tendência dos(das) desenvolvedores(as) JS de aprender "apenas o suficiente" para seguir em frente, sem se forçarem a entender exatamente a forma e o motivo pelos quais a linguagem se comporta da maneira que o faz. Além disso, evitamos o senso comum de recuar quando a estrada fica irregular ou perigosa.

Eu não fico feliz, nem você deveria ficar, em parar assim que algo funciona, sem realmente entender o porquê. Eu gentilmente desafio você a viajar para a parte "menos acessada" da estrada e se envolver com tudo que o JavaScript pode fazer. Com esse conhecimento, nenhuma técnica, nenhum framework, nenhum nome que virou moda na semana estará além da sua compreensão.

Cada um desses livros tem um enfoque específico em partes da linguagem que em geral são compreendidas de maneira incorreta ou mal compreendidas, e mergulha a fundo nelas. Você deverá finalizar a leitura com confiança em relação ao seu entendimento, não só das partes teóricas, mas também das partes que você "precisa saber".

O JavaScript que você conhece agora é um provável conjunto de partes ensinadas a você por outros(as) que também tiveram um conhecimento incompleto. Esse JavaScript não é nada além de uma sombra da verdadeira linguagem. Você não conhece realmente o JavaScript, ainda, mas se você caminhar por esta série você saberá. Continuem lendo, amigos(as). O JavaScript aguarda vocês.

Sumário

JavaScript é incrível! É fácil de aprendê-la parcialmente, e muito difícil aprendê-la por completo (ou até mesmo suficientemente). Quando desenvolvedores(as) se sentem confusos(as), geralmente culpam a linguagem ao invés de sua falta de conhecimento. Esses livros têm como objetivo corrigir isso, inspirando uma apreciação forte da linguagem que agora você pode (e deve) conhecer profundamente.

Nota: Muitos dos exemplos desse livro supõem que você dispõe de ambientes JavaScript modernos (e de longo alcance futuro), como o ES6. Alguns códigos podem não funcionar como descrito se você estiver utilizando ambientes mais antigos (pre-ES6).

You Don't Know JS: Up & Going

Capítulo 1: Iniciando

Bem vindo à série You Don't Know JS (YDKJS).

Iniciando é uma introdução à diversos conceitos básicos de programação -- claro que inclinado ao uso do JavaScript (muitas vezes abreviado como JS) especificamente -- e como abordar e entender o resto dos títulos nessa série. Especialmente se você está começando em programação ou JavaScript, esse livro irá, de forma breve, explorar tudo que você precisa saber para iniciar.

Esse livro começa explicando os princípios básicos de programação em uma camada mais alta. É mais indicado se você está começando YDKJS com pouca ou nenhuma experiência anterior com programação, e está procurando nesses livros uma ajuda para começar na longa jornada de entender como programar através da visão do JavaScript.

O Capítulo 1 deve ser abordado como uma visão geral de coisas que você irá querer aprender e praticar mais para iniciar na programação.Existem também outros recursos para entender melhor essa introdução à programação e eu encorajo você a aprender através deles em adição à este capítulo.

Uma vez que você se sentir confortável com os conceitos básicos da programação, o Capítulo 2 irá te familiarizar com o modo de programar com JavaScript. O Capítulo 2 faz uma introdução sobre do que o JavaScript é capaz, mas novamente, ele não é um guia compreensivo -- esse é a finalidade do resto da série YDKJS!

Se você já se sente confortável com JavaScript, dê uma olhada no Capítulo 3 como uma breve visão do que esperar com YDKJS, e caia de cabeça!

Código

Vamos começar do começo.

Um programa, também conhecido como código fonte ou apenas código, é um conjunto de instruções especializadas para dizer ao computador quais tarefas ele deve realizar. Geralmente, códigos são salvos em arquivos de texto, apesar de que, em JavaScript, você também pode escrever códigos direto no developer console do navegador, o qual iremos cobrir em breve.

As regras para formatos e combinações de instruções é chamado linguagem de computação, e algumas vezes é referenciado como sintaxe, bem parecido como a lingua Portuguesa diz à você a forma de pronunciar palavras e de como criar sentenças válidas usando palavras e pontuações.

Instruções

Em linguagens de computação, um grupo de palavras, números e operadores que realizam tarefas específicas são uma instrução. Em JavaScript, uma instrução pode ser parecida com o seguinte:

a = b * 2;

Os caracteres a e b são chamados variáveis (veja "Variáveis"), que são recipientes em que você pode armazenar qualquer coisa dentro. Em programas, variáveis detém valores (como o número 42) que serão utilizados pelo programa. Pense neles como nomes simbólicos para chamarmos os valores.

Em contrapartida, o 2 é apenas um valor, chamado valor literal, por que é apresentado sozinho, sem estar armazenado em uma variável.

Os caracteres = e * são operadores (veja "Operadores") -- eles realizam ações com os valores e variáveis como realizar uma atribuição qualquer ou uma multiplicação matemática.

A maioria das intruões em JavaScript termina com um ponto e vírgula (;) no final.

A instrução a = b * 2; diz ao computador, grosseiramente falando, para pegar o valor atual dentro da variável b, multiplicar esse valor por 2, depois armazenar o resultado dentro de outra variável, chamada a.

Programas são apenas coleções de muitas instruções, que juntas descrevem todas as etapas que são necessárias para cumprir a finalidade do programa.

Expressões

Instruções são feitas de uma ou mais expressões. Uma expressão é qualquer referência à uma variável ou valor, ou um conjunto de variáveis e valores combinados com operadores.

Por exemplo:

a = b * 2;

Essa instrução tem quatro expressões dentro dela:

  • 2 é uma expressão de valor literal
  • b é uma expressão de valor variável, que significa que ela armazena seu valor atual
  • b * 2 é uma expressão aritmética, que significa execute a multiplicação
  • a = b * 2 é uma expressão de atribuição, que significa designar o resultado da expressão b * 2 para a variável a (mais instruções depois).

Uma expressão genérica que permanece sozinha é também chamada de instrução de expressão, como o exemplo abaixo:

b * 2;

Esse tipo de instrução de expressão não é muito comum ou útil, como geralmente a instrução não afeta o desenvolvimento do programa -- ela apenas pega o valor armazenado por b e multiplica por 2, sem realizar nenhuma ação com esse resultado.

Uma instrução de expressão mais comum é chamada instrução de expressão de chamada (veja "Funções"), sendo a própria chamada da função uma instrução completa:

alert( a );

Executando um Programa

Como essas coleções de instruções em programação dizem ao computador o que fazer? O programa precisa ser executado, ou, mais comumente usado, precisamos rodar o programa.

Instruções como a = b * 2 são úteis quando desenvolvedores estão lendo e escrevendo, mas não são numa forma que o computador possa entender. Sendo assim, uma ferramenta especial (tanto um interpretador como um compilador) é usado para traduzir o código que você escreveu em comandos que o computador possa entender.

Para algumas linguagens, essa tradução dos comandos é típicamente feita de cima para baixo, linha por linha, cada vez que o programa roda. Essas etapas são geralmente chamadas de interpretação do código NT definem uma linguagem interpretada.

Para outras linguagens, a tradução é feita em tempos distintos, chamado compilamento do código. Dessa forma, o programa roda depois, ou seja: o que está rodando são as instruções prontas, já compiladas. NT Definem uma linguagem compilada.

Tipicamente, afirma-se que o JavaScript é uma linguagem interpretada, porque o código é processado a cada vez que roda. Essa afirmação não é totalmente verdadeira. Na verdade, a engine do JavaScript compila o programa no mesmo instante e imediatamente roda o código compilado.

Nota: Para mais informações sobre compilação em JavaScript, veja os dois primeiros capítulos do livro desta série Escopos & Encerramentos.

Tente você mesmo

Esse capítulo irá introduzir você em cada conceito de programação com snippets simples de código, todos escritos em JavaScript (claro!).

Nunca é demais enfatizar: enquanto você lê este capítulo -- e você poderá precisar vir aqui diversas vezes -- você deve praticar cada um desses conceitos escrevendo o código você mesmo. A forma mais fácil de fazer isso é abrir o console do developer tools do seu navegador favorito (Firefox, Chrome, IE, etc.).

Dica: Geralmente, você pode abrir o console com um atalho do teclado ou um item do menu. Para informações mais detalhadas sobre abrir e utilizar o console do seu navegador favorito, veja "Mastering The Developer Tools Console" (http://blog.teamtreehouse.com/mastering-developer-tools-console). Para digitar mais de uma linha no console, use <shift> + <enter> para mover para próxima linha. Uma vez digitado <enter>, o console irá rodar tudo que você digitou.

Vamos nos familiarizar com o processo de rodar o código no console. Primeiro, sugiro que abra uma aba em branco no seu navegador. Eu prefiro fazer isso digitando about:blank na barra de endereços. Feito isso, abra o console, como acabamos de mencionar.

Agora, digite o código abaixo e veja como ele se comporta:

a = 21;

b = a * 2;

console.log( b );

Digitar o código acima no console do Chrome deverá produzir algo parecido com isso:

logo

Vá em frente, tente também! A melhor forma de aprender programação é produzindo códigos!

Output

No exemplo anterior, usamos o console.log(..). Vamos, superficialmente, entender o que essa linha de código faz.

Você deve ter suspeitado: essa é exatamente a forma como imprimimos texto (também conhecido como output) no console do desenvolvedor.

Primeiro, a parte do log( b ) é usada como uma função de chamada (veja "Funções"). O que está acontecendo é que estamos usando a variável b na função para pegar seu valor e imprimir no console.

Depois, a parte do console. é uma referência ao objeto onde a função log(..)está localizada. Iremos cobrir objetos e suas propriedades com mais detalhes no Capítulo 2.

Outra forma de criar um output que você possa visualizar é rodar a instrução alert(..). Por exemplo:

alert( b );

Se você rodar esse comando, irá perceber que ao invés de imprimir o resultado no console, um popup com o conteúdo da variável b e um botão de "OK" irão aparecer. Entretanto, usar console.log(..) em geral vai facilitar seu aprendizado e a forma de rodar seus programas, mais do que se estivesse usando o alert(..), porque com o console.log(..) você pode expressar mais valores de uma vez sem interromper a interface do navegador.

Para esse livro, iremos usar sempre o console.log(..) para os nossos outputs.

Input

Enquanto estávamos discutindo sobre o output, você deve ter se perguntado sobre o input (em outras palavras, receber informações do usuário).

A forma mais comum de isso acontecer é através de um formulário em uma página HTML (como caixas de texto) para o usuário inserir suas informações e depois usar JS para ler esses valores nas variáveis do seu programa.

Mas existe uma maneira ainda mais fácil de conseguir um input com a finalidade de demonstração e aprendizado como as que você irá fazer ao longo desse livro. Usando a função prompt(..):

age = prompt( "Please tell me your age:" );

console.log( age );

Como você deve ter suspeitado, a mensagem a ser passada para o prompt(..) -- nesse caso, "Please tell me your age:" -- é impresso no popup.

O resultado deve ser parecido com a imagem abaixo:

logo

Uma vez que você enviar a informação ao clicar em "OK", observe que o valor que você digitou é armazenado na variável age, que nós então fazemos o output com console.log(..):

logo

Para manter as coisas simples enquanto estamos aprendendo os conceitos básicos da programação, os exemplos desse livro não irão precisar de input. Mas agora que você viu como usar o prompt(..), se você quiser se desafiar, pode tentar usar input ao explorar os exemplos.

Operadores

Operadores são como realizar uma ação em variáveis e valores. Nós já vimos até agora dois operadores em JavaScript, o = e o *.

O operador * realiza uma multiplicação matemática. Simples o suficiente, não?

O operador de igualdade = é usado para atribuir -- primeiro calculamos o valor do lado da mão direita (valor original) do = e então o colocamos em uma variável que especificamos no lado da mão esquerda (variável de destino).

Atenção: Essa pode parecer uma ordem reversa estranha de especificar um atribuição. Ao invés de a = 42, algumas pessoas preferem inverter a ordem do valor original na esquerda e a variável de destino na direita, algo como 42 -> a (isso não é JavaScript valido!). Infelizmente, a forma ordenada a = 42 e variações similares,prevalece em linguagens de programação modernas. Caso pareça uma forma não-natural, tome algum tempo assimilando essa forma na sua cabeça até se sentir acostumado.

Considere:

a = 2;
b = a + 1;

Aqui, atribuimos o valor2 à variável a. Assim, pegamos o valor da variável a (ainda 2), adicionamos 1 a ele, resultando no valor 3, então armazenamos esse valor na variável b.

Apesar de não ser tecnicamente um operador, você irá precisar da palavra-chave var em cada programa, por ser o primeiro modo de declarar (conhecido como criar) variáveis (veja "Variáveis").

Você sempre deve declarar a variável por nome antes de usá-la. Mas você precisa declarar a variável apenas uma vez para cada escopo (veja "Escopo"); ela pode ser usada depois quantas vezes forem necessárias. Por exemplo:

var a = 20;

a = a + 1;
a = a * 2;

console.log( a );	// 42

Aqui encontram-se os operadores mais comuns em JavaScript:

  • Atribuição: = como em a = 2.

  • Aritmético: + (adição), - (subtração), * (multiplicação), e / (divisão), como em a * 3.

  • Atribuição com Operação: +=, -=, *=, e /= são operadores comuns que combinam operadores aritméticos com a atribuição, como em a += 2 (o mesmo que a = a + 2).

  • Incremento/Decremento: ++ (incremento), -- (decremento), como em a++ (similar à a = a + 1).

  • Acesso a propriedade do Objeto: . como em console.log().

    Objetos são valores que armazenam outros valores em lugares determinados por nome, chamados propriedades. obj.a significa que este é um objeto chamado obj com uma propriedade de nome a. Propriedades podem, alternativamente, serem chamados por obj["a"]. Veja mais no capítulo 2.

  • Igualdade: == (igualdade), === (igualdade estrita), != (desigualdade), !== (desigualdade estrita), como em a == b.

    Veja "Valores & Tipos" e o Capítulo 2.

  • Comparação: < (menor que), > (maior que), <= (menor ou igual), >= (maior ou igual), como em a <= b.

    Veja "Valores & Tipos" e o Capítulo 2.

  • Lógicos: && (e), || (ou), como em a || b que seleciona a ou b.

    Esses operadores são usados para expressar instruções condicionais (veja "Condicionais"), como se a ou b for verdadeiro.

Note: Para muito mais detalhes, e cobertura dos operadores não mencionados aqui, veja mais no Mozilla Developer Network (MDN)'s "Expressões e Operadores" (https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Expressions_and_Operators).

Valores & Tipos

Se você abordar uma vendedora de uma loja de celulares e perguntar quanto um certo modelo custa, e ela disser "noventa e nove e noventa e nove" ($99.99), ele está fornecendo um valor numérico que representa quanto você vai precisar pagar para comprar o aparelho. Se você quiser levar dois desses celulares, você pode facilmente fazer uma conta mental e dobrar o valor para encontrar o valor $199,98.

Se a vendedora pegar outro aparelho similar e disser "é grátis", ela não está te fornecendo um valor numérico, mas está fazendo um outro tipo de representação de um valor que é esperado ($0.00) -- a palavra "grátis."

Se você depois perguntar se o aparelho vem com carregador, a resposta pode ser apenas "sim" ou "não."

De modo bem similar, quando você expressa valores em um programa, você escolhe representações diferentes para aqueles valores baseado no que você planeja fazer com eles.

Essas diferentes representações para valores são chamados tipos na terminologia de programação. JavaScript tem tipos pré-definidos para cada um dos chamados valores primitivos:

  • Quando quiser fazer operações matemáticas, você vai precisar de um number.
  • Quando você precisar imprimir um valor na tela, você precisará de uma string (um ou mais caracteres, palavras, sentenças).
  • Quando você precisar tomar uma decisão em seu programa, você vai precisar de um boolean (true ou false).

Valores que são incluídos diretamente no código fonte são chamados literais. Literais string são sempre envolvidas por aspas duplas "..." ou aspas simples ('...') -- a única diferença é a preferência estética. Literais number e boolean são apresentadas como são (exemplos: 42, true, etc.).

Considere:

"I am a string";
'I am also a string';

42;

true;
false;

Além de tipos como string/number/boolean, é comum para linguagens de programação proverem arrays, objetos, funções, e mais. Iremos cobrir muito mais sobre valores e tipos ao longo desse capítulo e também do próximo.

Coerções entre Tipos

Se você tem um número(number) mas precisa imprimí-lo na tela, você precisará converter o valor para uma string, e em JavaScript isso é chamado de "coerção." De maneira similar, se alguém insere uma série de caracteres numéricos em um formulário de uma página de ecommerce, isso é uma string, mas se você precisar usar esse valor para fazer operações matemáticas, você vai precisar converter para um numero(number).

O JavaScript fornece diversas facilidades para forçar a coerção entre tipos. Por exemplo:

var a = "42";
var b = Number( a );

console.log( a );	// "42"
console.log( b );	// 42

Usando Number(..) (uma função nativa) como demonstrado, estamos realizando uma coerção explícita de qualquer outro tipo para o tipo number (número). Isso deve ser bem claro.

Um tópico controverso acontece quando você tenta comparar dois valores que ainda não são do mesmo tipo, que requer uma coerção implícita.

Quando comparada a string "99.99" com o número 99.99, muitos concordam que elas sejam equivalentes. Mas ele não são exatamente iguais, são? É o mesmo valor em duas representações diferentes, dois tipos diferentes. Você poderia dizer que eles são "igualdade nao-estrita", não poderia?

Para te ajudar nessas situações, o JavaScript irá, em alguns casos, implicitamente converter os valores para os tipos certos.

Sendo assim, se você usar o operador de igualdade não-estrita == para fazer uma comparação entre "99.99" == 99.99, o JavaScript vai converter o lado da mão esquerda "99.99" para seu número(number) equivalente 99.99. A comparação então se torna 99.99 == 99.99, que é claro, é verdadeira (true).

Apesar de ter sido feito para te ajudar, coerções implícitas geram confusão se você não teve tempo de aprender as regras que regem seu comportamento. A maioria dos desenvolvedores de JS nunca tiveram, então o sentimento geral é que coerções implícitas são confusas e deixam os programas com bugs inesperados, e os mesmos devem ser evitados. Em alguns casos até o design da linguagem é considerado falho.

Entretanto, coerções implícitas é um mecanismo que pode ser aprendido, e mais ainda deve ser aprendido por qualquer um que queira levar a programação em JavaScript a sério. Não apenas as coerções não são confusas, uma vez que aprendidas as regras, como pode fazer os seus programas melhores! Os esforços para aprender valerão a pena.

Nota: Para mais informações sobre coerções, veja o Capítulo 2 desse título (Iniciando) e o Capítulo 4 de Tipos & Gramática da série.

Comentários do Código

A vendedora da loja de celulares pode escrever algumas notas sobre funcionalidades de um recém-lançado aparelho ou sobre os novos planos que a empresa oferece. Essas notas são apenas para ela ler -- elas não são feitas para os consumidores lerem. De qualquer forma, essas notas ajudam a vendedora a fazer seu trabalho por documentar os "como's" e "porquê's" do que deve-se falar aos consumidores.

Uma das lições mais importantes que você pode aprender sobre códigos é que eles não são apenas para computadores. O código é feito tanto, senão mais, para o desenvolvedor do que para o compilador.

Seu computador se importa apenas com código de máquina, uma série de binários, 0s e 1s, que vem da compilação. Existe uma infinidade de programas que você pode escrever que produzem as mesmas séries de 0s e 1s. As escolhas que você faz sobre como programar importam -- não apenas para você, mas para toda a equipe que você está trabalhando e até para você mesmo no futuro.

Você deve se empenhar não apenas em escrever programas que funcionam corretamente, mas programas que fazem sentido ao serem examinados. Você pode percorrer uma boa parte desse caminho começando por escolher bons nomes para variáveis (veja "Variáveis") e funções (veja "Funções").

Uma parte importante do nosso código são os comentários. Eles são blocos de texto no seu programa que são inseridos com o propósito único de explicar coisas a um humano. O interpretador/compilador sempre irá ignorar esses comentários.

Existem diversas opiniões sobre o que faz um código ser bem documentado; não podemos definir regras universais. Entretanto, algumas observações e orientações são bastante úteis:

  • Códigos sem comentários não são ideais.
  • Muitos comentários (um por linha, por exemplo) é provavelmente sinal de código mal escrito.
  • Comentários devem explicar porquê e não o quê. Eles podem opcionalmente explicar como se a parte for particularmente confusa.

Em JavaScript, existem dois tipos possíveis de comentário: uma linha simples de comentário e um comentário multi-linhas.

Considere:

// Esse é um comentário de linha simṕles

/* Mas esse é
       um comentário
             multi-linhas.
                      */

O // comentário de linha simples é apropriado se você está fazendo um comentário logo acima de uma instrução, ou até mesmo no final da linha. Tudo após o início da linha de // é tratado como um comentário (e consequentemente ignorado pelo compilador), até o final da linha. Não existem restrições sobre o que pode ser posto dentro de um comentário de linha simples.

Considere:

var a = 42;		// 42 é o sentido da vida

O /* .. */ comentário de multi-linhas é apropriado se você precisar de diversas linhas de explicações para fazer seu comentário.

Abaixo uma forma comum de usar um comentário de multiplas linhas:

/* O valor abaixo é usado porque
   foi exposto que ele responde
   a todas as questões do universo. */
var a = 42;

O comentário multi-linhas pode inclusive aparecer em uma linha, ou até mesmo no meio de uma linha, porque o */ finaliza ele. Por exemplo:

var a = /* valor arbitrário */ 42;

console.log( a );	// 42

Porém a única coisa que não pode aparecer dentro de um comentário multi-linhas é um*/, porque seria interpretado como final do comentário.

Você definitivamente irá querer começar seu aprendizado na programação com o hábito de comentar seu código. Através desse capítulo, você verá que uso comentários para explicar coisas, então faça isso com suas práticas. Confie em mim, todos que irão ler seu código vão agradecer!

Variáveis

A maioria dos programas que são úteis, precisam rastrear um valor conforme ele se modifica ao longo do programa, realizando diferentes operações enquanto o programa o chama de acordo com determinadas tarefas.

A forma mais fácil de realizar esse procedimento é determinar um valor para um agregador simbólico, chamado variável -- sendo assim chamado porque o valor que carrega pode variar ao longo do tempo se necessário.

Em algumas linguagens de programação, você declara a variável (agregador) para manter um valor específico, como por exemplo um number ou string. A Tipagem estática, é geralmente citada como benéfica para o programa por previnir coerções de valores não desejadas.

Outras linguagens enfatizam tipos para valores ao invés de variáveis. A Tipagem fraca, também conhecida como tipagem dinâmica, permite que uma variável armazene qualquer tipo de valor em qualquer tempo. É tipicamente citada como benéfica para a flexibilidade de um programa, por permitir que uma simples variável tenha um valor representado independentemente do tipo e do momento dentro decorrer da lógica.

O JavaScript usa a segunda abordagem, tipagem dinâmica, o que significa que as variáveis podem armazenar valores de qualquer tipo.

Como mencionado anteriormente, nós declaramos uma variável usando a instrução var -- note que não existe aqui nenhuma informação sobre tipo nessa declaração. Considere esse simples programa:

var amount = 99.99;

amount = amount * 2;

console.log( amount );		// 199.98

// converte o amount` para uma string, e
// adiciona "$" no começo
amount = "$" + String( amount );

console.log( amount );		// "$199.98"

A variável amount começa armazenando o número 99.99, e depois armazena o resultado númerico(number) de amount * 2, que é 199.98.

O primeiro comando e console.log(..) precisa implicitamente converter o valor de number para string para poder imprimí-lo.

A instrução amount = "$" + String(amount) explicitamente converte o valor 199.98 para uma string e adiciona um caractere "$" para o começo. Nesse ponto, amount agora armazena o valor em string de "$199.98", então o segundo comando console.log(..) não precisa fazer nenhuma coerção para imprimir seu valor.

Os desenvolvedores JavaScript irão notar a flexibilidade de usar a variável amount para cada um dos valores 99.99, 199.98 e "$199.98". Entusiastas da tipagem estática irão preferir separar valores como amountStr para armazenar a representação do valor "$199.98", por ser de um tipo diferente.

De qualquer forma, você irá notar que amount armazena um valor corrente que é alterado conforme o decorrer do programa, ilustrando assim o primeiro propósito das variáveis: gerenciar o estado do programa.

Em outras palavras, estado é o acompanhamento das mudanças dos valores conforme seu programa está rodando.

Outro exemplo comum de como usar uma variável é quando você deseja definir as opçoes de valores. Isso é geralmente chamado de constante, quando você declara uma variável com um valor e deseja que o valor não mude ao longo do programa.

Você declara essas constantes geralmente no início do programa, de forma a se tornar um lugar conveniente de se visitar caso deseje alterar algum valor. Por convenção, variáveis definidas como constantes em JavaSscript são geralmente capitalizadas e separadas por um sublinhado.

Abaixo um exemplo simples:

var TAX_RATE = 0.08;	// 8% de taxas

var amount = 99.99;

amount = amount * 2;

amount = amount + (amount * TAX_RATE);

console.log( amount );				// 215.9784
console.log( amount.toFixed( 2 ) );	// "215.98"

Nota: Assim como console.log(..) tem a função log(..) acessada como uma propriedade do valor do objeto de console, toFixed(..)é uma função que pode ser acessada para valores number. O number em JavaScript não é automaticamente formatado para ser o valor de uma moeda -- o sistema não sabe o que se pretende fazer e não existe um tipo específico para moedas. toFixed(..) nos deixa especificar quantos valores decimais gostaríamos que o number fosse arredondado, e ele produz a string como necessário.

A variável TAX_RATE só é uma constante por convenção -- não existe nada especial nesse programa que não permita que ela seja alterada. Mas se a cidade aumentar o valor das taxas para 9%, nós ainda poderemos atualizar o valor de TAX_RATE para 0.09 no mesmo lugar, ao invés de procurar diversas ocorrências do valor 0.08 ao longo do programa e ter que atualizá-los um por um.

A nova versão do JavaScript no momento que escrevo isso (comumente chamada de "ES6") inclui uma nova forma de declarar constantes, usando const ao invés de var:

// como em ES6:
const TAX_RATE = 0.08;

var amount = 99.99;

// ..

Constantes são tão úteis quanto variáveis de valores imutáveis, exceto que constantes também previnem que valores sejam alterados acidentalmente após sua configuração inicial. Se você tentar designar diferentes valores para TAX_RATE após sua primeira declaração, seu programa vai rejeitar a mudança (e em modo estrito, irá gerar um erro -- veja "Modo Estrito" no Capítulo 2).

Falando nisso, o tipo de "proteção" contra acidentes é similar ao de linguagens com tipagem estática, assim você vê como a tipagem estática em outras linguagens pode ser bem atrativa!

Nota: Para mais informações sobre os diferentes valores em variáveis que podem ser usados em seus programas, veja o título Tipagem & Gramática dessa série.

Blocos

A vendedora da loja de celulares precisa seguir uma série de etapas para efetivar a compra do seu novo celular.

De maneira similar, em código nós muitas vezes precisamos agrupar uma série de instruções, as quais podemos chamar de blocos. Em JavaScript, um bloco é definido por englobar uma ou mais instruções dentro de um par de chaves { .. }. Considere:

var amount = 99.99;

// um bloco qualquer
{
	amount = amount * 2;
	console.log( amount );	// 199.98
}

Essa forma de formatação do bloco{ .. } é válida, mas não é muito comum de se ver em programas em JS. Tipicamente, blocos são anexados a outros tipos de controle, como dentro de uma condicional ìf (veja "Condicionais") ou em um laço (veja "Loops"). Por exemplo:

var amount = 99.99;

// amount é grande o suficiente?
if (amount > 10) {			// <-- bloco anexado ao `if`
	amount = amount * 2;
	console.log( amount );	// 199.98
}

Iremos explicar condicionais if na próxima seção, mas como você pode ver, o bloco { .. }com suas duas instruções é anexado ao if (amount > 10); as instruções dentro do bloco só irão ser processadas se a condicional for aceita.

Nota: Ao contrário da maioria das instruções como console.log(amount);, uma instrução de bloco não precisa de um ponto-e-vírgula (;) para ser concluída.

Condicionais

"Você gostaria de adicionar protetores de tela extras à sua compra, por $9.99". A atenciosa vendedora da loja de celulares fez você tomar uma decisão. E você primeiro precisa consultar o estado corrente da sua conta bancária para responder à essa pergunta. Mas, obviamente, essa é uma questão de um simples "sim" ou "não".

Existem diversas formas das quais podemos expressar condicionais (ou decisões) em nossos programas.

A forma mais comum é a condicional if. Essencialmente, você está dizendo, "se essa condição for verdadeira, faça isso...".

var bank_balance = 302.13;
var amount = 99.99;

if (amount < bank_balance) {
	console.log( "Quero comprar esse celular!" );
}

A condicional if requer uma expressão entre parênteses ( ) que pode ser definida como verdadeira (true) ou falsa (false). Nesse programa, declaramos a expressão amount < bank_balance, que irá determinar se o valor é true ou false, dependendo da quantidade dentro da variável bank_balance.

Você pode até mesmo definir uma alternativa para se a condição não for verdadeira, uma cláusula chamada else. Considere:

const ACCESSORY_PRICE = 9.99;

var bank_balance = 302.13;
var amount = 99.99;

amount = amount * 2;

// podemos fazer uma compra extra?
if ( amount < bank_balance ) {
	console.log( "Vou levar este acessório!" );
	amount = amount + ACCESSORY_PRICE;
}
// se não pudermos:
else {
	console.log( "Não, obrigado." );
}

Aqui, se amount < bank_balance for true, iremos imprimir "Vou levar este acessório!" e adicionar 9.99 para a nossa variável amount. Ou senão pudermos, a cláusula else diz que podemos responder, polidamente, "Não, obrigado." e deixar o amount inalterado.

Como discutimos em "Valores & Tipos" anteriormente, valores que não são de algum tipo anteriormente definido, geralmente é coergido para o novo tipo. Se a condicional if esperar um tipo boolean, mas o argumento que você passou for de algum tipo que não seja boolean, uma coerção irá acontecer.

O JavaScript define uma lista de valores específicos que são considerados "falsinhos" porque quando coergido para boolean, eles se tornam false -- esses valores incluem 0 e "". Qualquer outro valor não incluído na lista de "falsinhos" será automaticamente definido como "verdadeirinho" -- quando coergidos para boolean se tornam true. Valores verdadeirinhos incluem coisas como 99.99 e "free". Veja "Verdadeirinhos & Falsinhos" no Capítulo 2 para mais informaçoes.

Condicionais existem em outras formas além do if. Por exemplo, a instrução switch pode ser usada como um atalho para uma série de instruções if..else (veja o Capítulo 2). Os Loops (veja "Loops") usam uma condicional para determinar se um loop deve prosseguir rodando ou parar.

Nota: Para conhecer mais a fundo sobre coerções que podem ocorrer implicitamente ao testar expressões em condicionais, veja o Capítulo 4 do título desta série Tipos & Gramática.

Loops

Durante dias movimentados, existe uma lista de espera de consumidores que precisam falar com a vendedora da loja de celulares. Enquanto houverem pessoas na lista, ela precisa continuar atendendo o próximo consumidor.

Repetir uma série de ações até que certa condição falhe -- em outras palavras, repetir apenas enquanto a condição for respeitada -- é o trabalho dos loops na programação (NT: traduzido também como laços); os loops podem ter diversas formas, mas todos eles têm esse mesmo comportamento.

Um loop inclui a condição teste, assim como o bloco (tipicamente um { .. }). A cada vez que o bloco de loop é executado, damos o nome de iteração.

Por exemplo, o loop while e o loop do..while ilustram o conceito de repetir um bloco de instruções até uma condição deixar de ser verdadeira (true):

while (numOfCustomers > 0) {
	console.log( "Como posso ajudar?" );

	// Ajude o consumidor...

	numOfCustomers = numOfCustomers - 1;
}

// versus:

do {
	console.log( "Como posso ajudar?" );

	// Ajude o consumidor...

	numOfCustomers = numOfCustomers - 1;
} while (numOfCustomers > 0);

A única diferença prática entre esses dois loops é como a condicional é executada, se antes da primeira iteração (while) ou após a primeira iteração (do..while).

De qualquer forma, se o teste da condicional retorna falso (false), a próxima iteração não irá rodar. Isso significa que se a condição inicial for false, um loop while nunca irá rodar, mas um loop do..while irá rodar apenas a primeira vez.

Às vezes você está fazendo um loop com a finalidade de contar uma certa quantidade de números, como de 0 à 9 (dez números). Você pode fazer isso definindo uma iteração utilizando uma variável i para o valor 0 e ir incrementando 1 para cada iteração.

Atenção: Por diversas questões históricas, linguagens de programação quase sempre começam a contar pelo zero, o que significa que elas começam com 0 ao invés de 1. Se você não é familiar com essa forma de pensar, pode ser um pouco confuso no início. Tire um tempo para praticar contando a partir do 0 para se ambientar com essa forma.

A condicional é testada em cada iteração, mesmo se existir uma condicional if dentro do loop.

Podemos usar a instrução breakpara parar um loop. Além disso, podemos observar que é terrivelmente fácil criar um loop que pode rodar para sempre sem um mecanismo que o faça parar.

Vamos ilustrar isso:

var i = 0;

// um loop `while..true` iria rodar pra sempre, certo?
while (true) {
	// parar o loop?
	if ((i <= 9) === false) {
		break;
	}

	console.log( i );
	i = i + 1;
}
// 0 1 2 3 4 5 6 7 8 9

Atenção: Essa não é necessariamente uma forma prática que você usaria para executar para seus loops. Ela é apresentada aqui com propósito ilustrativo apenas.

Enquanto os loops while (ou do..while) podem fazer a tarefa manualmente, existe outra forma, outro loop chamado for que serve justamente para essa finalidade:

for (var i = 0; i <= 9; i = i + 1) {
	console.log( i );
}
// 0 1 2 3 4 5 6 7 8 9

Como você pôde ver, nos dois casos a condicional i <= 9 foi verdadeira(true) para as 10 primeiras iterações (i para os valores de 0 até 9) em ambas as formas do loop, mas se torna falsa(false) uma vez que o valor de i chega a 10.

O loop for tem três instruções: uma atribuição inicial (var i=0), um teste condicional (i <= 9), e uma atualização (i = i + 1). Sendo assim, se o que você pretende fazer com a iteração é uma contagem, for é a forma mais compacta e em geral mais fácil de entender e escrever.

Existem outros loops especializados que são designados a iterar sobre valores específicos, como propriedades de um objeto (veja o Capítulo 2) onde a aplicação do teste condicional é saber se todas as propriedades foram processadas. O conceito de "iterar até determinada condição falhar" permanece independentemente do formato do loop.

Funções

A vendedora da loja de celulares provavelmente não anda com uma calculadora para saber a quantidade de taxas e o valor final do produto. Essa é uma tarefa que ela precisa definir uma vez e reusar diversas vezes. As chances são que a empresa que ela trabalha tenha um aparelho (computador, tablet, etc) que tenha, de fábrica, esse tipo de funcionalidade.

De maneira similar, o programa quase certamente irá dividir o código em partes reusáveis, ao invés de ficar se repetindo. A forma que fazemos isso é definindo uma função (function).

Uma função geralmente é uma bloco de código nomeado, de forma que ela possa ser "chamada" pelo nome, fazendo o código dentro dela ser acionado sempre que preciso. Considere:

function printAmount() {
	console.log( amount.toFixed( 2 ) );
}

var amount = 99.99;

printAmount(); // "99.99"

amount = amount * 2;

printAmount(); // "199.98"

Funções podem, opcionalmente, carregar argumentos (conhecidos também como parâmetros) -- valores que você designa a ela. E eles podem, também opcionalmente, retornar um outro valor.

function printAmount(amt) {
	console.log( amt.toFixed( 2 ) );
}

function formatAmount() {
	return "$" + amount.toFixed( 2 );
}

var amount = 99.99;

printAmount( amount * 2 );		// "199.98"

amount = formatAmount();
console.log( amount );			// "$99.99"

A função printAmount(..) utiliza um parâmetro que chamamos amt. A função formatAmount() retorna um valor. Claro, você também pode combinar essas duas técnicas na mesma função.

Funções são geralmente usadas para códigos que você planeja chamar diversas vezes, mas eles podem ser úteis também para organizar códigos relacionados em coleções que você possa nomear, mesmo que você só planeja chamá-los apenas uma vez.

Considere:

const TAX_RATE = 0.08;

function calculateFinalPurchaseAmount(amt) {
	// calcula o novo amount adicionando a tax
	amt = amt + (amt * TAX_RATE);

	// retorne o novo amount
	return amt;
}

var amount = 99.99;

amount = calculateFinalPurchaseAmount( amount );

console.log( amount.toFixed( 2 ) );		// "107.99"

Apesar de calculateFinalPurchaseAmount(..) ser chamado apenas uma vez, organizar seu comportamento em uma função separadamente faz o código que usa sua lógica (a instrução amount = calculateFinal...) mais limpa. Se a função tiver mais instruções nela, os benefícios podem ser ainda maiores.

Escopo

Se você pedir à vendedora da loja de celulares por um modelo que não está em estoque, ela não poderá te vender o celular que você quer. Ela só tem acesso aos aparelhos que estão em estoque, você terá que ir até outra loja para saber se eles têm o telefone que você deseja.

Em programação temos um termo para esse conceito: escopo (tecnicamente chamado escopo léxico). Em JavaScript, cada função tem seu próprio escopo. O escopo é basicamente uma coleção de variáveis e regras de como essas variáveis serão acessadas pelo nome. Apenas o código dentro dessa função poderá as variáveis dentro daquele escopo.

O nome de uma variável precisa ser único dentro do escopo -- não podem haver duas variáveis diferentes com o nome a no mesmo escopo. Porém duas variáveis com o nome a em escopos diferentes podem coexistir sem problemas.

function one() {
	// essa variável `a` só pertence à função `one()`
	var a = 1;
	console.log( a );
}

function two() {
	// essa variável `a` só pertence à função `two()`
	var a = 2;
	console.log( a );
}

one();		// 1
two();		// 2

Também podemos aninhar um escopo dentro de outro escopo, igual a um palhaço numa festa de aniversário e estoura um balão que contém outro balão dentro. Se um escopo está aninhado a outro, o código dentro do escopo interno pode acessar as variáveis do escopo mais externo.

Considere:

function outer() {
	var a = 1;

	function inner() {
		var b = 2;

		// nós podemos acessar ambos `a` e `b` aqui
		console.log( a + b );	// 3
	}

	inner();

	// podemos acessar apenas `a` aqui
	console.log( a );			// 1
}

outer();

As regras do escopo léxico dizem que o código dentro de um escopo pode acessar variáveis tanto dele mesmo quanto de qualquer escopo fora dele.

Assim, considere que a função inner() tem acesso a ambas as variáveis a e b, mas o código dentro de outer() só tem acesso à a -- ele não pode acessar b porque a variável está dentro de inner().

Voltando ao exemplo anterior:

const TAX_RATE = 0.08;

function calculateFinalPurchaseAmount(amt) {
	// calcula o novo amount adicionando a tax
	amt = amt + (amt * TAX_RATE);

	// retorne o novo amount
	return amt;
}

A constante TAX_RATE (variável) é acessível dentro da função calculateFinalPurchaseAmount(..), mesmo se não passarmos por ela, por conta do escopo léxico.

Nota: Para mais informações sobre o escopo léxico, veja os primeiros três capítulos dos títulos dessa série Escopos & Encerramentos.

Pratique

Não existe absolutamente nenhum substituto para prática ao aprender à programar. Nenhuma quantidade de artigos de minha parte, sozinhos, irão fazer você se tornar um programador.

Com isso em mente, vamos tentar praticar alguns conceitos que aprendemos neste capítulo. Eu irei lhe dar os "requerimentos", e você primeiro irá tentar executá-los. Depois, consulte o código listado abaixo para ver a forma que eu os resolvi.

  • Escreva um programa que calcula o preço total da compra do seu celular. Você pode continuar comprando telefones (dica: loop!) até você ficar sem dinheiro na sua conta bancária. Você irá também comprar acessórios para cada telefone enquanto sua quantidade de dinheiro for menor do que seu limite mental.
  • Após calcular o valor da compra, adicione as taxas, depois imprima a quantidade total, devidamente formatada.
  • Por fim, verifique o total gasto em sua conta bancária para saber se você pode comprar ou não.
  • Você deve definir algumas constantes para a "taxa de imposto", "preço do telefone", "preço do acessório", e "limite de gastos", assim como variáveis para o seu "saldo bancário".
  • Você deve definir funções para calcular a taxa e para formatar o preço com um "$" e arredondá-lo para duas casas decimais.
  • Desafio Extra: Tente incorporar um input para esse programa, talvez com oprompt(..) que abordamos anteriormente em "Input". Você pode definir um prompt para o usuário para definir o saldo de sua conta bancária, por exemplo. Divirta-se e seja criativo!

Certo, vá em frente. Tente. Não venha ver o resultado do código que fiz enquanto você não tentar por conta própria!

Nota: Por este ser um livro de JavaScript, eu obviamente estarei resolvendo o exercício em JavaScript. Mas você pode fazer por enquanto em qualquer linguagem que você se sentir mais confortável.

Abaixo minha solução para esse exercício:

const SPENDING_THRESHOLD = 200;
const TAX_RATE = 0.08;
const PHONE_PRICE = 99.99;
const ACCESSORY_PRICE = 9.99;

var bank_balance = 303.91;
var amount = 0;

function calculateTax(amount) {
	return amount * TAX_RATE;
}

function formatAmount(amount) {
	return "$" + amount.toFixed( 2 );
}

// continue comprando até não ter mais dinheiro
while (amount < bank_balance) {
	// compre um novo celular!
	amount = amount + PHONE_PRICE;

	// podemos comprar o acessório?
	if (amount < SPENDING_THRESHOLD) {
		amount = amount + ACCESSORY_PRICE;
	}
}

// não esqueça a fatia do governo!
amount = amount + calculateTax( amount );

console.log(
	"Sua compra: " + formatAmount( amount )
);
// Sua compra: $334.76

// Você pode pagar a conta?
if (amount > bank_balance) {
	console.log(
		"Você não pode pagar a conta. :("
	);
}
// Você não pode pagar a conta. :(

Nota: A maneira mais fácil de rodar esse programa em JavaScript é digitá-lo no console do seu navegador mais próximo.

Como você foi? Não dói se você tentar de novo agora que viu meu código. Brinque um pouco alterando algumas constantes e veja como o programa roda com valores diferentes.

Recaptulando

Aprender a programar não precisa ser um processo complexo e cansativo. Existem apenas alguns conceitos que você precisa entender para as coisas começarem a fazer sentido.

Eles funcionam como blocos de montar. Para construir uma torre alta, você precisa primeiro colocar bloco em cima de bloco. O mesmo acontece com programação. Aqui encontram-se alguns blocos essenciais para programação:

  • Você precisa de operadores para modificar valores.
  • Você precisa de valores e tipos para realizar tipos deferentes de alterações como operações matemáticas em números (numbers) ou em um output com strings.
  • Você precisa de variáveis para armazenar informação (conhecido como estado) durante a execução de um programa.
  • Você precisa de condicionais como if para tomar decisões.
  • Você precisa de loops para repetir tarefas até uma condição deixar de ser verdadeira (true).
  • Você precisa de funções (function) para organizar seu código de forma lógica e em componentes reutilizáveis.

Comentários no código são uma maneira efetiva de escrever um código mais legível, o que irá fazer seu programa fácil de entender, manter e consertar caso tenha algum problema no futuro.

Por fim, não esqueça o poder da prática. A melhor forma de aprender como escrever código é escrevendo código.

Estou empolgado que você está progredindo na sua caminhada para aprender a programar. Continue assim. Não esqueça de ver também outros recursos para começar a programar (blogs, outros livros, treinamento online, etc.) Este capítulo e este livro são um grande começo, mas são apenas uma breve introdução.

No próximo capítulo iremos revisar muitos dos conceitos deste capítulo, mas de uma perspectiva mais específica para o JavaScript, que irá destacar a maioria dos grandes tópicos que iremos abordar mais à fundo ao longo da série.

You Don't Know JS: Iniciando

Capítulo 2: Por dentro do JavaScript

No capítulo anterior, fiz uma introdução básica sobre os blocos construtores da programação, como variáveis, loops, condicionais e funções. Claro, todo o código demonstrado foi em JavaScript. Mas neste capítulo, iremos focar especificamente no que deve ser aprendido em JavaScript para começarmos como um desenvolvedor JS.

Iremos introduzir alguns conceitos neste capítulo que não serão totalmente explorados até a sequência dos próximos livros desta série. Você pode pensar nesse capítulo como uma visão geral dos tópicos que serão abordados ao longo dos outros livros.

Especialmente se você for novo ao JavaScript, você pode esperar utilizar boa parte do seu tempo revisando por diversas vezes os conceitos e exemplos de código abordados. Toda boa fundação é feita de tijolo em tijolo, então não espere entender de imediato a medida que for progredindo em sua leitura.

Sua jornada para entender a fundo JavaScript começa aqui.

Nota: Como havia dito no Capítulo 1, você deve definitivamente testar por conta própria todos os códigos apresentados enquanto você estiver lendo através do capítulo. Tome nota que alguns dos códigos escritos aqui utilizam capacidades introduzidas na nova versão do JavaScript no momento em que estou escrevendo (comumente chamado de "ES6" por ser a 6ª edição do ECMAScript -- o nome oficial da especificação JS). Caso aconteça de você estar utilizando um navegador antigo, pre-ES6, o código pode não funcionar. Uma versão atualizada de um navegador moderno (como Chrome, Firefox, or IE) deverá ser usada.

Valores e Tipos

Como definimos no Capítulo 1, JavaScript tem valores tipados, não variáveis tipadas. Os seguintes tipos nativos estão disponíveis:

  • string
  • number
  • boolean
  • null e undefined
  • object
  • symbol (novidade do ES6)

O JavaScript dispõe de um operador typeofque pode examinar um valor e dizer a você qual é o tipo informado:

var a;
typeof a;               // "undefined"

a = "hello world";
typeof a;               // "string"

a = 42;
typeof a;               // "number"

a = true;
typeof a;               // "boolean"

a = null;
typeof a;               // "object" -- weird, bug

a = undefined;
typeof a;               // "undefined"

a = { b: "c" };
typeof a;               // "object"

O valor que é retornado pelo operador typeofé sempre um dos seis (sete com o ES6!) valores em string. Isso é, typeof "abc" retorna "string", não string.

Note como nesse snippet a variável a contém cada tipo diferente tipo de valor, e apesar de parecer, typeof a não está perguntando pelo "tipo de a", mas sim pelo "tipo de valor atualmente armazenado em a." Apenas valores possuem tipos em JavaScript; variáveis são apenas containers para esses valores.

typeof null é um caso interessante, porque erradamente retorna um "objeto", enquanto você espera que ele retorne "null".

Atenção: Esse é um bug antigo em JS, mas um do tipo que é provável de nunca ser consertado. Muitos códigos na Web dependem desse bug e portanto consertá-lo iria trazer ainda mais bugs!

Além disso, note que a = undefined. Nós explicitamente indicamos a para o valor undefined, mas a forma com que se comporta não é diferente de uma variável que não tem valor definido, como a linha var a;no topo do snippet. Uma variável pode chegar a esse valor "undefined" de diversas maneiras, incluindo funções que não retornam valores e o uso do operadorvoid.

Objetos

O tipo object se refere a um valor composto onde você pode definir propriedades (lugares nomeados prontos para armazenar informação) que podem armazenar seus próprios valores de qualquer tipo. Esse é talvez um dos tipos de valor mais úteis em todo JavaScript:

var obj = {
    a: "hello world",
    b: 42,
    c: true
};

obj.a;      // "hello world"
obj.b;      // 42
obj.c;      // true

obj["a"];   // "hello world"
obj["b"];   // 42
obj["c"];   // true

Talvez seja útil pensar nesse valor obj visualmente:

logo

Propriedades podem ser acessadas tanto com notação com ponto (dot notation, ex: obj.a) quanto por notação em colchetes (ex: obj["a"]). A notação por ponto é menor e geralmente mais fácil de ser lida, e por isso é a notação preferida, sempre que possível.

A notação em colchetes é útil caso você tenha um nome de propriedade que contenha caracteres especiais nele, como obj["hello world!"] -- esses tipos de propriedades são geralmente referenciadas com chaves (keys) quando acessadas por notação em colchetes. A notação [ ] requer ou uma variável (explicarei a seguir) ou uma string literal (que precisa ser englobada em " .. " ou ' .. ').

É claro, a notação em colchetes também é útil se você quiser acessar uma propriedade/chave onde o nome é armazenado dentro de outra variável, como por exemplo:

var obj = {
    a: "hello world",
    b: 42
};

var b = "a";

obj[b];         // "hello world"
obj["b"];       // 42

Nota: Para mais informação sobre objetos (objects) em JavaScript, veja o título desta série this & Prototipagem de Objetos, especificamente o Capítulo 3.

Existem outros tipos de valores que você pode facilmente interagir com programas em JavaScript: array e function. Mas ao invés de serem tipos nativos (built-in), eles devem ser vistos mais como sub-tipos -- versões especializadas do tipo object.

Arrays

Uma array é um object que armazena valores (qualquer tipo de valor), não necesariamente em propriedades ou chaves nomeadas, mas em posições enumeradas. Por exemplo:

var arr = [
    "hello world",
    42,
    true
];

arr[0];         // "hello world"
arr[1];         // 42
arr[2];         // true
arr.length;     // 3

typeof arr;     // "object"

Nota: Linguagens que começam contando do zero, como JS faz, usam 0 como índice do primeiro elemento da array.

Talvez seja útil pensar sobre arr visualmente:

logo

Visto que arrays são objetos especiais (assim como typeof sugere), elas também podem conter propriedades, incluindo a propriedade length que é automaticamente atualizada.

Em teoria, você pode usar uma array como um objeto normal com suas próprias propriedades nomeadas, ou pode usar um object e apenas determinar propriedades numéricas (0, 1, etc.) de maneira similar a uma array. Entretanto, isso geralmente é considerado uma maneira indevida de utilizar seus respectivos tipos.

A melhor maneira (e a mais natural) é utilizar arrays para valores posicionados numericamente e usar objects para propriedades nomeadas.

Funções

O outro subtipo de object que você usará ao longo de seus programas é uma função:

function foo() {
    return 42;
}

foo.bar = "hello world";

typeof foo;         // "function"
typeof foo();       // "number"
typeof foo.bar;     // "string"

Novamente, funções são um subtipo de objects -- o typeof retorna "function", que indica que function é um tipo padrão -- e por isso pode ter propriedades. Entretanto, é provável que você use as propriedades do objeto de function (como foo.bar) apenas em alguns casos.

Nota: Para mais informações sobre valores em JS e seus tipos, veja os primeiros dois capítulos do título Tipos & Gramática, desta série.

Métodos de Tipos Nativos

Os tipos e subtipos nativos que acabamos de ver tem alguns comportamentos bem úteis, expostos como propriedades e métodos.

Por exemplo:

var a = "hello world";
var b = 3.14159;

a.length;               // 11
a.toUpperCase();        // "HELLO WORLD"
b.toFixed(4);           // "3.1416"

é mais complicado do que apenas o método existente em um valor.

Resumidamente, existe uma forma agregadora do objeto String (S maiúsculo), que pareia com o tipo primitivo string. É esse objeto agregador que define o método toUpperCase() em seu protótipo.

Quando você usa um valor primitivo, como "hello world", como um object referenciando a propriedade ou método (exemplo a.toUpperCase() no snippet anterior), o JS automaticamente "encaixota" o valor na contraparte do agregador de seu objeto (escondido).

Um valor string pode ser englobado por um objeto String, um number pode ser englobado por um objeto Number, e um boolean pode ser englobado por um objeto Boolean. Para a maioria dos casos, você não precisa se preocupar sobre isso ou usar diretamente essas formas de agregar os valores do objeto -- preferindo a forma de valores primitivos em todos os casos que puder e o JavaScript vai cuidar do resto pra você.

Nota: Para mais informações em nativos em JS e formas de "encaixotar", veja o Capítulo 3 do título deste livro Tipos e Gramática. Para melhor entendimento dos protótipos de um objeto, veja o Capítulo 5 do título this & Object Prototypes.

Comparando Valores

Existem dois tipos principais de comparação de valores que você irá precisar para fazer seus programas em JS: igualdade e desigualdade. O resultado de qualquer comparação é estritamente um valor boolean (true ou false), independente do tipo de valor comparado.

Coerção

Falamos brevemente sobre coerção no Capítulo 1, mas vamos revisitá-lo aqui.

A coerção vem em duas formas em JavaScript: explicita e implicita. A coerção explícita é a forma que você pode, obviamente, através do código, que uma conversão de um tipo para o outro vai acontecer, e a coerção implícita é quando o tipo de conversão ocorre como um efeito paralelo, não tão óbvio, de alguma outra operação.

Você provavelmente ouviu coisas como "coerção é do mau", por conta da surpresa nos resultados que algumas situações específicas podem causar. Talvez nenhuma outra situação frustre mais um desenvolvedor do que quando a linguagem o surpreende.

Coerções não são do mau, nem mesmo devem ser surpreendentes. De fato, a maioria dos casos que você pode construir com a coerção de tipos são bem sensíveis e entendíveis, e podem até mesmo serem usados como maneira de melhorar a legibilidade do código. Mas não iremos entrar muito nesse debate -- O Capítulo 4 do título Tipos e Gramática desta série cobre bem essa parte.

Aqui temos um exemplo de coerção explícita:

var a = "42";

var b = Number( a );

a;              // "42"
b;              // 42 -- o número!

E aqui um exemplo de coerção implícita:

var a = "42";

var b = a * 1;  // "42" implicitamente coagido para 42 aqui

a;              // "42"
b;              // 42 -- o número!

Truthy & Falsy

No Capítulo 1, nós mencionamos brevemente a natureza "truthy" e "falsy" dos valores: quando um valor não-boolean é coagido para um valor boolean, ele se torna de fato true ou false?

A lista de valores "falsy" em JavaScript é a seguinte:

  • "" (string vazia)
  • 0, -0, NaN (number inválido)
  • null, undefined
  • false

Qualquer valor que não esteja nessa lista de "falsy", é considerado "truthy". Aqui estão alguns exemplos deles:

  • "hello"
  • 42
  • true
  • [ ], [ 1, "2", 3 ] (arrays)
  • { }, { a: 42 } (objects)
  • function foo() { .. } (functions)

É importante lembrar que um valor não-boolean segue a coerção como "truthy"/"falsy" apenas se ele for coagindo para boolean. Não é difícil se confundir com uma situação onde parece que estamos coagindo um valor para um boolean quando na verdade não estamos.

Igualdade

Existem quatro operadores de igualdade: ==, ===, !=, e !==. A forma ! é a versão simétrica de "não igual" de suas contrapartes; não-igualdade não deve ser confundido com desigualdade.

A diferença entre == e === é geralmente caracterizada por == verificar a igualdade de valores e === verificar a igualdade do valor e do tipo. Entretanto, essa forma não é a mais apurada. A maneira correta de caracterizá-los é que == verifica por igualdade com coerção autorizada, e === verifica a igualdade do valor sem autorizar a coerção; === é comumente chamada de "igualdade estrita" por essa razão.

Considere a coerção implícita que é autorizada pelo comparador de igualdade == e não permitido com a igualdade estrita ===:

var a = "42";
var b = 42;

a == b;         // true
a === b;        // false

Na comparação a == b, o JS percebe que os tipos não combinam, então ele segue uma sequência de etapas para coagir um ou ambos os valores para um tipo diferente até que os tipos combinem, de forma que um valor de igualdade simples possa ser considerado.

Se você pensar sobre isso, não existem dois modos possíveis onde a == b possa dar true por coerção. Ou a comparação por se dar por 42 == 42 ou ela pode ser "42" == "42". Sendo assim, qual das duas é a correta?

Resposta: "42" se torna 42, para fazer a comparação 42 == 42. Nesse exemplo simples, não parece importante saber qual processo será, no final o resultado é o mesmo. Existem casos mais complexos onde não apenas importa qual é o resultado final como foi possível chegar lá.

A igualdade a === b produz um resultado false, porque a coerção não é permitida, assim obviamente a comparação falha. Muitos desenvolvedores pregam que === é mais previsível, permanecendo usando sempre esta forma e ficando longe de ==. Acho esse ponto de vista limitado. Acredito que == é uma ferramenta poderosa que pode ajudar você em seus programas, se você se dedicar a aprender como ele funciona.

Não vamos nos aprofundar nos detalhes de como a coerção em comparações com == funciona. Muito sobre ela é bem intuitivo, mas existem casos específicos importantes de se tomar nota. Você pode ler a seção 11.9.3 da especificação do ES5 (http://www.ecma-international.org/ecma-262/5.1/) para ver suas regras exatas, e você ficará surpreso em como seu mecanismo é bem desenvolvido, comparado a toda hype negativa à sua volta.

Para resumir um monte de detalhes em passos bem simples e ajudar você a decidir sobre usar == ou === em várias situações, aqui vão minhas regras simples:

  • Se em um dos lados da comparação você puder ter um valor true ou false, evite == e use ===.
  • Se em um dos lados da comparação você puder ter esses valores específicos (0, "", ou [] -- array vazia), evite == e use ===.
  • Em todos os outros casos, você estará seguro usando ==. Não apenas é mais seguro, mas em muitos casos simplifica seu código de forma a melhorar sua leitura.

O que essas regras fazem é obrigar você a pensar criticamente sobre seu código e quais tipos de valor podem aparecer através de variáveis que são comparadas pela igualdade. Se você estiver certo desses valores, e == é seguro, use-o! Se você não pode estar certo dos resultados, use ===. É simples assim.

A não igualdade !=forma um par com ==, e sua forma !== forma um par com ===. Todas as regras e observações que discutimos até aqui funcionam de maneira simétrica para essas comparações de não-igualdade.

Você deve ter uma atenção especial sobre as regras de comparação de == e === se você estiver comparando dois valores não-primitivos, como objects (incluíndo function e array). Por estes valores serem regidos por suas referências, ambas as comparações == e === irão apenas verificar se suas referências são compatíveis, e não irá comparar nada sobre seus valores subjacentes.

Por exemplo, arrays são por padrão convertidas parastrings por simplesmente se juntarem todos os valores com vírgulas (,) entree elas. Você pode pensar que duas arrays com o mesmo conteúdo são iguais ==, quando na verdade não são:

var a = [1,2,3];
var b = [1,2,3];
var c = "1,2,3";

a == c;     // true
b == c;     // true
a == b;     // false

Nota: Para mais informações sobre as regras de comparação de igualdade ==, veja a especificação do ES5 (seção 11.9.3) e também consulte o Capítulo 4 do título desta série Tipos & Gramática; veja o Capítulo 2 sobre mais informações sobre valores versus referências.

Desigualdade

Os operadores <, >, <=, e >= são usados para representar uma desigualdade, sendo referenciados na especificação como "comparadores relacionais". Tipicamente eles são usados para comparar valores ordinários como numbers. É fácil entender que 3 < 4.

Mas em JavaScript, valores string também podem ser comparados para desigualdade, usando regras alfabéticas ("bar" < "foo").

E como fica a coerção: Regras similares à comparação == (apesar de não serem idênticas!) aplicam-se aos operadores de desigualdade. Uma nota importante, é que não existe um operador de "desigualdade estrita" que possa desabilitar a coerção da mesma forma que a "igualdade estrita" === faz.

Considere:

var a = 41;
var b = "42";
var c = "43";

a < b;      // true
b < c;      // true

O que acontece aqui: Na seção 11.8.5, da especificação do ES5, ela diz que ambos os valores na comparação < são strings, assim como em b < c, a comparação é feita lexicograficamente (em outras palavras: alfabeticamente, como um dicionário). Mas se um ou ambos os valores não forem uma string, como acontece em a < b, então ambos os valores são coagidos para serem numbers, e uma comparação típica de números acontece.

A maior pegadinha que você pode encontrar aqui é em comparações entre diferentes tipos de valores -- lembrando, não existem formas de usar uma "desigualdade estrita" -- é quando um dos valores não pode ser transformado em um número válido, como por exemplo:

var a = 42;
var b = "foo";

a < b;      // false
a > b;      // false
a == b;     // false

Espera, como podem as três comparações serem false? Porque o valor de b é coagido para um "valor numérico inválido" (NaN) nas comparações < e >, e a especificação diz que NaN não é nem maior nem menor do que qualquer valor.

A comparação == falha por uma razão diferente. a == b pode falhar se for interpretada tanto como 42 == NaN ou como "42" == "foo" -- como explicamos anteriormente.

Nota: Para mais informações sobre as regras de comparação de desigualdade, veja a seção 11.8.5 da especificação ES5 e também consulte o Capítulo 4 de Tipos e Grámaticas dessa série.

Variáveis

Em JavaScript, nome de variáveis (incluindo nomes de funções) precisam ter identificadores válidos. As restritas e completas regras para caracteres válidos como identificadores são um pouco complexas considerando os caracteres não tradicionais como Unicode. Se você considerar apenas caracteres alfanuméricos típicos do ASCII, as regras ficam mais simples.

Um identificador precisa começar com a-z, A-Z, $, ou _. Ele pode conter qualquer um desses caracteres e incluem também numerais 0-9.

Em geral, as mesmas regras que se aplicam tanto para identificar variáveis como para nomear uma propriedade. Entretanto, algumas palavras não podem ser usadas para nomear variáveis, mas não tem impedimento para nomear propriedades. Essas palavras são denominadas "palavras reservadasˆ(reserved words), e incluem as palavras-chave (for, in, if, etc.) assim como null, true, e false.

Nota: Para mais informações sobre palavas reservadas, veja o Apêndice A do título desta série Tipos & Gramática.

Escopos de Função

Você usa a palavra-chave var para declarar a variável que irá referenciar o escopo da função corrente, ou o escopo global se ela estiver no topo de tudo, fora de qualquer outra função.

Hoisting

Onde quer que var apareça dentro de um escopo, sua declaração é tomada como parte de todo o escopo e acessada em qualquer área dentro dele.

Metaforicamente, esse comportamento é chamado de hoisting, quando uma declaração var é conceitualmente "movida" para o topo do escopo. Tecnicamente, este processo é explicado de forma mais apurada entendendo como o código é compilado, mas vamos pular estes detalhes por ora.

Considere:

var a = 2;

foo();                  // funciona porque a declaração `foo()`
                        // é "hoisted"

function foo() {
    a = 3;

    console.log( a );   // 3

    var a;              // a declaração é "hoisted"
                        // para o topo de `foo()`
}

console.log( a );   // 2

Atenção: não é comum nem uma boa ideia confiar no hoisting de variáveis para utilizar uma determinada variável dentro do seu escopo antes de sua declaração aparecer no código; pode ficar um tanto confuso. É muito mais comum e aceito utilizar funções que sofreram hoisting em sua declaração, assim como fazemos com a chamada para foo() que aparece antes de sua declaração formal.

Escopos Aninhados

Quando você declara uma variável, ela é disponibilizada em todos os lugares dentro do escopo, assim como dentro de qualquer escopo interno. Por exemplo:

function foo() {
    var a = 1;

    function bar() {
        var b = 2;

        function baz() {
            var c = 3;

            console.log( a, b, c ); // 1 2 3
        }

        baz();
        console.log( a, b );        // 1 2
    }

    bar();
    console.log( a );               // 1
}

foo();

Note que c não está disponível dentro de bar(), porque está declarado dentro do escopo de baz(), e o b não está disponível para foo() pelo mesmo motivo.

Se você tentar acessar o valor da variável dentro de um escopo onde ela não está disponível, você irá receber um erro de ReferenceError. Se você tentar setar uma variável que ainda não foi declarada, ou você terminará criando uma variável no escopo global (ruim!) ou irá gerar um erro (caso tenha declarado "strict mode", veja "Strict Mode"). Vamos dar uma olhada:

function foo() {
    a = 1;  // `a` não foi formalmente declarada
}

foo();
a;          // 1 -- oops, você acaba de criar uma variável global automática :(

Esta é uma prática muito ruim. Não faça isso! Sempre declare suas variáveis formalmente.

Além de criarmos declarações de variáveis no mesmo nível da função, o ES6 deixa (let) você criar variáveis que irão pertencer a blocos individuais (pares de { .. }), usando a palavra-chave let. Apesar de suas nuances e detalhes, as regras do escopo terão o comportamento bem parecido com o que vimos em funções.

function foo() {
    var a = 1;

    if (a >= 1) {
        let b = 2;

        while (b < 5) {
            let c = b * 2;
            b++;

            console.log( a + c );
        }
    }
}

foo();
// 5 7 9

Por usarmos let ao invés de var, b irá pertencer apenas à instrução if e não para todo o escopo da função foo(). De maneira similar, c pertence somente ao loop while. Escopamentos de bloco são muito úteis para controlar seus escopos de variáveis, usando uma maneira requintada, o que faz seu código muito mais fácil de manter ao longo do tempo.

Nota: Para mais informações sobre escopos, veja o título desta série Escopos & Clausuras. Veja o título ES6 & Além para mais informações sobre o bloco de escopo let.

Condicionais

Em adição à instrução condicional if que introduzimos brevemente no Capítulo 1, o JavaScript nos fornece alguns outros mecanismos de condicionais que devemos saber.

Por vezes você se encontrará escrevendo uma série de instruções if..else..if como estas:

if (a == 2) {
    // faça alguma coisa
}
else if (a == 10) {
    // faça outra coisa
}
else if (a == 42) {
    // faça outra coisa diferente
}
else {
    // resultado se nenhuma instrução for atendida (fallback)
}

Essa estrutura funciona, mais é um pouco verbosa porque você precisa especificar um teste para a em cada um dos casos. Abaixo veremos uma outra opção, a instrução switch:

switch (a) {
    case 2:
        // faça alguma coisa
        break;
    case 10:
        // faça outra coisa
        break;
    case 42:
        // faça outra coisa diferente
        break;
    default:
        // resultado se nenhuma instrução for atendida (fallback)
}

O break é importante se você quiser que apenas uma instrução seja executada em cadacase. Se você omitir o break de um case, e esse case for aceito ou rodar, a execução irá continuar pelos próximos case's independente do case que foi aceito. Esse então chamado "fall through" é por vezes útil/proposital:

switch (a) {
    case 2:
    case 10:
        // alguma coisa legal
        break;
    case 42:
        // outra coisa
        break;
    default:
        // resultado se nenhuma instrução for atendida (fallback)
}

Aqui, se a for ou 2 ou 10, iremos executar a instrução de código "some cool stuff".

Uma outra forma de condicional em JavaScript é o "operador condicional," chamado também de "operador ternário." Ele é uma forma concisa/simplificada de uma instrução if..else, como em:

var a = 42;

var b = (a > 41) ? "hello" : "world";

// similar a:

// if (a > 41) {
//    b = "hello";
// }
// else {
//    b = "world";
// }

Se a expressão teste (a > 41 aqui) for avaliada como true, a primeira cláusula ("hello") é retornada, se não for, a segunda cláusula é retornada ("world"), e qualquer que seja o resultado ele é designado para b.

O operador condicional não precisa necessariamente ser usado em uma atribuição, mas é definitivamente a forma mais comum de usá-lo.

Nota: Para mais informações sobre testes de condições e outros padrões para switch e ? :, veja o título desta série Tipos & Gramática.

Modo Estrito (Strict Mode)

O ES5 adicionou o "strict mode" para a linguagem, que determina regras mais rígidas para certos comportamentos. Geralmente essas restrições são vistas como algo que faz o código se tornar mais seguro e com padrões melhor definidos. Além disso, aderindo ao modo estrito, em geral, seu código será melhor otimizado pelo Motor. O strict mode é uma grande vitória para o código, e você deveria usá-lo em todos os seus programas.

Você pode optar pelo modo estrito em uma função individualmente, ou em todo um arquivo, dependendo de onde você determinar o pragma do modo estrito:

function foo() {
    "use strict";

    // este código usa o modo estrito

    function bar() {
        // este código está em modo estrito
    }
}

// este código não está em modo estrito

Compare isso com:

"use strict";

function foo() {
    // este código está em modo estrito

    function bar() {
        // este código está em modo estrito
    }
}

// este código está em modo estrito

Uma diferença chave (melhoria!) com o strict mode é desabilitar a variável auto-global implícita ao omitir o var:

function foo() {
    "use strict";   // liga o strict mode
    a = 1;          // `var` faltando: ReferenceError
}

foo();

Se você habilitar o modo estrito em seu código, e você receber erros, ou o código se comporta de maneira bugada, a tentação é evitar o modo estrito. Mas esse instinto é uma má ideia de se deixar acontecer. Se o modo estrito gera problemas no seu programa, é certo que isso é um sinal que existem coisas que você deve consertar.

O modo estrito não irá apenas deixar seu código em um caminho mais seguro, também deixará seu código mais otimizável e também representando o futuro da linguagem. É mais fácil se acostumar com o modo estrito agora do que deixá-lo de lado -- vai ser mais difícil se converter a ele mais tarde!

Nota: Para mais informações sobre o modo estrito, veja o Capítulo 5 do título desta série Tipos & Gramática.

Funções como Valores

Até agora, discutimos funções como um mecanismo primário do escopo em JavaScript. Você deve se lembrar da típica sintaxe da declaração function desse jeito:

function foo() {
    // ..
}

Apesar de não parecer óbvio por essa sintaxe, foo é basicamente apenas uma variável que referência um escopo por onde é feita a referência para a função (function) que está sendo declarada. Isso é, a function por si só é um valor, assim como 42 ou [1,2,3] podem ser.

Pode parecer um conceito estranho de primeira, então vamos tirar um tempo para ponderar. Você não apenas pode passar um valor (argumento) para a função, mas a função por conta própria pode ser um valor que pode ser designado a uma variável, ou, passado para ou retornado por, outras funções.

Sendo assim, o valor de uma função deve ser pensado como uma expressão, assim como qualquer outro valor ou expressão.

Considere:

var foo = function() {
    // ..
};

var x = function bar(){
    // ..
};

A primeira expressão da função designada para a variável foo é chamada de anônima porque não tem nome.

A segunda expressão da função é nomeada (bar), mesmo que ela tenha sido referenciada para a variável x. Expresões de Funções Nomeadas são geralmente preferidas, apesar de expressões de funções anônimas serem extremamente comuns.

Para mais informações, veja o título desta série Escopos & Clausuras.

Expressões de Função Invocadas Imediatamente (IIFEs)

No snippet anterior, nenhuma das expressões de função são executadas -- poderíamos executá-las se incluíssemos foo() ou x(), por exemplo.

Existe uma outra forma de executar uma expressão de função, que é tipicamente chamada de Expressões de Função Invocadas Imediatamente (immediately invoked function expression, ou IIFE):

(function IIFE(){
    console.log( "Hello!" );
})();
// "Hello!"

O ( .. ) externo que rodeia a expressão de função (function IIFE(){ .. }) é apenas uma nuance que a gramática do JS precisa para prevenir que seja tratada como uma declaração de uma função qualquer.

O final () no fim da expressão -- a linha })(); -- é o que atualmente executa a expressão da função referenciada logo em seguida a ela.

Isso pode parecer estranho, mas não é tão desconhecido em uma primeiro olhar. Considere as similaridades entre foo e IIFE abaixo:

function foo() { .. }

// referência de expressão `foo`,
// depois `()` executa ele
foo();

// expressão da função `IIFE`,
// depois `()` executa ele
(function IIFE(){ .. })();

Como você pode ver, listar (function IIFE(){ .. }) antes de sua execução () é essencialmente a mesma coisa do que executar foo antes de sua execução (); em ambos os casos, a referência da função é executada com o () logo em seguida.

Porque IIFE é apenas uma função, e funções criam variáveis scope, usar uma IIFE dessa forma é muitas vezes usado para declarar variáveis que não afetarão o código fora de IIFE:

var a = 42;

(function IIFE(){
    var a = 10;
    console.log( a );   // 10
})();

console.log( a );       // 42

IIFEs também podem retornar valores:

var x = (function IIFE(){
    return 42;
})();

x;  // 42

O valor 42 é returnado da função nomeada IIFE, e depois designada à variável x.

Clausura

A Clausura (Closure) é um dos mais importantes, e muitas vezes incompreendido, conceitos em JavaScript. Eu não irei cobri-lo em detalhes aqui, deixando para fazer uma melhor referência no título desta série Escopos e Clausuras. Porém, gostaria de dizer algumas coisas relacionadas a ele para que você possa ter uma visão geral do conceito. Esta será uma das habilidades mais importantes do seu vocabulário em JS.

Você pode pensar em uma clausura como uma forma de "lembrar" e continuar acessando o escopo de uma função (e suas variáveis) mesmo se a função já estiver terminado de rodar.

Considere:

function makeAdder(x) {
    // parâmetro `x` é uma variável interna

    // função interna `add()` usa `x`, então
    // ele tem uma "clausura" que o envolve
    function add(y) {
        return y + x;
    };

    return add;
}

A referêcia para a função interna add(..), que faz um retorno com cada chamada para sua função externa makeAdder(..) é apta a se lembrar sempre que o valor x for passado para makeAdder(..). Agora, vamos usar o makeAdder(..):

// `plusOne` pega a referência para a função interna add(..)`
// função com clausura sobre o parâmetro `x` da
// função externa `makeAdder(..)`
var plusOne = makeAdder( 1 );

// `plusTen` pega a referência para a função interna `add(..)`
// função com clausura sobre o parâmetro `x` da
// função externa `makeAdder(..
var plusTen = makeAdder( 10 );

plusOne( 3 );       // 4  <-- 1 + 3
plusOne( 41 );      // 42 <-- 1 + 41

plusTen( 13 );      // 23 <-- 10 + 13

Mais sobre como esse código funciona:

  1. Quando chamamos makeAdder(1), temos de volta a referência para a função add(..) que se lembra de x como 1. Chamamos a referência dessa função de plusOne(..).
  2. Quando chamamos makeAdder(10), temos de volta outra referência para a função interna add(..) que se lembra de x como 10. Nós chamamos a referência dessa função de plusTen(..).
  3. Quando chamamos plusOne(3), ele adiciona 3 (seu y interno) ao 1 (lembrado por x), e temos 4 como resultado.
  4. Quando nós chamamos plusTen(13), ele adiciona 13 (seu y interno) ao 10 (lembrado por x), e nós conseguimos 23 como resultado.

Não se preocupe se parecer estranho e confuso no começo -- pode ser mesmo! Vai precisar de muita prática para entender completamente.

Mas acredite em mim, uma vez que conseguir entender, vai perceber que é uma das mais poderosa e mais úteis técnicas em toda linguagem. É definitivamente válido o esforço de deixar seu cérebro acostumado com clausuras. Na próxima seção, vamos ter um pouco mais de prática com clausuras.

Módulos

A forma de uso mais comum de um clausura (closure) em JavaScript é o padrão módulo (module pattern). Módulos deixam você definir detalhes privados de implementação (variáveis, funções) que estarão escondidas do mundo externo, assim como uma API pública que é acessível de fora.

Considere:

function User(){
    var username, password;

    function doLogin(user,pw) {
        username = user;
        password = pw;

        // faça o resto do trabalho do login
    }

    var publicAPI = {
        login: doLogin
    };

    return publicAPI;
}

// cria uma instãncia do módulo`User`
var fred = User();

fred.login( "fred", "12Battery34!" );

A função User() serve como um escopo externo que mantém as variáveis username e password protegidas, assim como a função interna doLogin(); esses itens são todos detalhes internos desse módulo User que não podem ser acessados de fora.

Atenção: Nós não estamos chamando new User() aqui, de propósito. Apesar do fato de que provavelmente esta ser a forma mais comum para a maioria dos usuários. User() é apenas uma função, não uma classe a ser instanciada, então ela é chamada normalmente. Usar new poderia ser inapropriado e até mesmo um desperdício de recursos.

Executar User() cria uma instância do módulo User -- todo um novo escopo é criado, e assim toda uma nova cópia de cada uma das variáveis/funções internas. Nós designamos essa instância para fred. Se nós rodarmos User() novamente, teríamos uma nova instância completamente separada do fred.

A função interna doLogin() tem uma clausura sobre username e password, significando que ela iria reter seus acessos à eles mesmo após a função User() terminar.

publicAPI é um objeto com uma propriedade/método nela, o login, que é uma referência à função interna doLogin(). Quando nós retornamos publicAPI de User(), ele se torna a instância que chamamos de fred.

Neste ponto, a função externa User() já terminou de ser executada. Normalmente, você pensaria que variáveis internas como username e password teriam se perdido. Mas não estão, porque existe uma clausura (closure) na função login() que os mantém vivos.

É por isso que chamamos fred.login(..) -- o mesmo que chamar a função interna doLogin(..) -- e ela ainda assim pode acessar as variáveis internas username e password.

Existe uma boa chance que com apenas este resumo breve sobre clausuras e o padrão módulo (module pattern), alguma coisa sobre o assunto ainda pareça confuso. Está tudo bem! Precisa de alguma prática para seu cérebro passar a entendê-los.

Daqui, vá para o título desta série Escopos e Clausuras para uma exploração do assunto muito mais profunda.

Identificador this

Outro conceito comumente incompreendido em JavaScript é o identificadorthis. Novamente, existem um bocado de capítulos sobre ele no título desta série *this & Prototipagem de Objetos", então aqui iremos introduzir apenas superficialmente o conceito.

Enquanto geralmente possa parecer que this está relacionado aos padrões de orientação à objetos, em JS o this é um mecanismo diferente.

Se uma função tiver uma referência ao this dentro dela, esse this geralmente aponta para um object. Mas qual objeto que this aponta irá depender de como a função é chamada.

É importante entender quethis não se refere à função propriamente dita, visto que essa é a parte mais comumente confundida.

Aqui uma ilustração rápida:

function foo() {
    console.log( this.bar );
}

var bar = "global";

var obj1 = {
    bar: "obj1",
    foo: foo
};

var obj2 = {
    bar: "obj2"
};

// --------

foo();              // "global"
obj1.foo();         // "obj1"
foo.call( obj2 );   // "obj2"
new foo();          // undefined

Existem quatro regras de como this é definido, e como ele é apresentado nessas quatro últimas linhas do snippet acima.

  1. foo() termina definindo this para o objeto global em modo não-estrito -- no modo estrito (strict mode), this seria undefined e você receberia um erro ao acessar a propriedade bar -- então "global" é o valor encontrado para this.bar.
  2. obj1.foo() define this para o objeto obj1.
  3. foo.call(obj2) define this para o objeto obj2.
  4. new foo() define this para um objeto completamente novo.

Fim da linha: para entender o que this aponta, você precisa examinar como a função em questão é chamada. Ela será uma das quatro formas demonstradas, e assim você irá ter a resposta do que é this.

Nota: Para mais informações sobre this, veja os Capítulos 1 e 2 do título desta série this & Prototipagem de Objetos.

Prototipagem

O mecanismo de protótipos em JavaScript é bem complicado. Aqui vamos só vê-lo de relance. Você irá precisar de algum tempo revisando os Capítulos 4-6 do título desta série this & Prototipagem de Objetos para saber todos os detalhes.

Quando você referencia uma propriedade em um objeto, se essa propriedade não existir, o JavaScript irá automaticamente usar aquela referência ao protótipo interno do objeto para achar outro objeto procurando pela propriedade em si. Você pode pensar nisso como um fallback para o caso da propriedade estar faltando.

A referência da relação ao protótipo interno de um objeto para o seu fallback acontece no tempo em que o objeto é criado. A maneira mais simples de ilustrar isso é com sua utilidade nativa chamada Object.create(..).

Considere:

var foo = {
    a: 42
};

// cria `bar` e faz o link para `foo`
var bar = Object.create( foo );

bar.b = "hello world";

bar.b;      // "hello world"
bar.a;      // 42 <-- delegado para `foo`

Talvez ajude visualizando os objetos foo e bar e sua relação:

logo

A propriedade a não existe atualmente no objeto bar, mas conta de bar ser prototipalmente associada a foo, o JavaScript automaticamente falls back para procurar por a no objeto foo, onde o encontra.

Essa linkagem (associação) pode parecer uma funcionalidade estranha na linguagem. A forma mais comum que essa funcionalidade é usada -- e eu digo até, abusada -- é ao tentar emular um mecanismo de "classe" com "herança".

Mas uma forma mais natural de aplicar prototipagens é em um padrão chamado "delegação de comportamento", onde você intencionalmente desenha as associações de seus objetos para serem habilitadas a delegar de uma a outra parte do comportamento desejado.

Nota: Para mais informações sobre prototipagens e delegação de comportamentos, veja os Capítulos 4-6 do título desta série this & Prototipagem de Objetos.

Velho & Novo

Algumas das funcionalidades de JS que já cobrimos, e certamente muitas das funcionalidades que cobriremos no resto dessa série, são novas adições que não necessariamente estarão disponíveis em navegadores antigos. De fato, algumas das novas funcionalidades disponíveis na especificação nem mesmo foram implememntadas em navegadores estáveis ainda.

Então, o que você faz com as coisas novas? Você só precisa esperar por anos ou décadas por todos os navegadores antigos apagarem e sumirem na escuridão?

Isso é como muitas pessoas pensam sobre essa situação, mas não é realmente uma abordagem saudável para o JS.

Existem duas técnicas principais que você pode usar para "trazer" as coisas novas de JavaScript para os navegadores antigos: polyfilling e transpiling.

Polyfilling

A palavras "polyfill" é um termo inventado (por Remy Sharp) (https://remysharp.com/2010/10/08/what-is-a-polyfill) usado para referenciar a definição de uma nova funcionalidade e reproduzir um pedaço de código que é equivalente ao comportamento, mas que pode rodar em ambientes com o JS antigo.

Por exemplo, o ES6 define uma utilidade chamada Number.isNaN(..) para prover um check acurado e livre de bugs para valores NaN, deixando obsoleto a utilidade original isNaN(..). Mas é fácil criar um polyfill para essa utilidade de forma que você possa usar no seu código independente do usuário final estar usando um navegador com suporte ao ES6 ou não.

Considere:

if (!Number.isNaN) {
    Number.isNaN = function isNaN(x) {
        return x !== x;
    };
}

A instrução if deixa de aplicar a definição do polyfill em navegadores ES6 onde ela já existe. Se não estiver presente, nós definimos Number.isNaN(..).

Nota: A verificação que fazemos aqui toma vantagem de um equívoco com valores NaN, que é o único valor na linguagem que não é igual a ele mesmo. Então o valor NaN é o único que pode fazer x !== x ser true.

Nem todas as novas funcionalidades são completamente polyfillável. Às vezes a maior parte do comportamento pode ser polyfillada, mas com alguns desvios. Você deve ter muito, muito cuidado em implementar um polyfill por conta própria, para ter certeza que está aderindo à especificação da forma mais estrita possível.

Ou melhor ainda, use um vetado grupo de polyfills que você pode confiar, como os providos pelo ES5-Shim (https://github.com/es-shims/es5-shim) e ES6-Shim (https://github.com/es-shims/es6-shim).

Transpiling

Não existe nenhuma forma de polyfillar uma nova sintaxe, que ainda não foi incluída na linguagem. A nova sintaxe iria retornar um erro no mecanismo do JS como não reconhecida/inválida.

Sendo assim a melhor opção é usar uma ferramenta que converte seu código novo em um código antigo equivalente. Esse processo é comumente chamado "transpiling".

Essencialmente, seu código fonte é feito com o formato da nova sintaxe, mas quando você faz o deploy para o navegador ele aparece como um código transpilado com o formato da sintaxe velha. Tipicamente você insere o transpilador no seu processo de build, similar ao seu linter ou minifier.

Você pode se perguntar porquê você se meteria em confusão ao escrever uma sintaxe nova, apenas para vê-la sendo transpilada para o código antigo. Porquê então não escrever da forma antiga diretamente?

Existem diversas razões importantes que você deveria se importar em relação ao transpiling:

  • A nova sintaxe adicionada à linguagem é desenhada para fazer seu código ser mais legível e fácil de manter. Os equivalentes mais antigos são geralmente muito mais debilitados. Você deve optar por escrever a sintaxe mais moderna e clara, não apenas para você mas para todos os membros do seu time de desenvolvimento.

  • Se você transpilar apenas para navegadores antigos, mas servir a nova sintaxe para os navegadores mais novos, você teria uma vantagem por conta da otimização da performance do navegador devido a nova sintaxe. Isso também permite que os criadores dos browsers tenham códigos do mundo-real (em produção) para testar neles suas implementações e otimizações.

  • Usando a nova sintaxe de forma precoce faz com que ela possa ser testada de forma mais robusta no mundo real, o que dá um feedback prematuro para o comitê do JavaScript (TC39). Se problemas forem encontrados brevemente, eles podem ser alterados/consertados antes que esses erros de design da linguagem serem implementados permanentemente.

Aqui um breve exemplo de transpiling. O ES6 adiciona uma funcionalidade chamada "valor padrão de parâmetro". Ele se parece com isso:

function foo(a = 2) {
    console.log( a );
}

foo();      // 2
foo( 42 );  // 42

Simples, não? Útil, também! Mas é uma nova sintaxe que é inválida em mecanismos pre-ES6. Então o que faria um transpilador para esse código rodar em ambientes mais antigos?

function foo() {
    var a = arguments[0] !== (void 0) ? arguments[0] : 2;
    console.log( a );
}

Como você pode ver, ele faz uma verificação para saber se o valor de arguments[0] é void 0 (vulgo undefined), e se for define 2 como valor padrão; se for outra forma, ele designa qualquer que seja o valor informado.

Em adição ao fato de poder usar agora uma sintaxe mais bacana, mesmo em navegadores mais antigos, olhar para o código transpilado explica o comportamento esperado de forma mais clara.

Você não deve ter reparado apenas olhando para a versão do ES6 que undefined é o único valor que não pode ser passado explicitamente como um valor padrão para um parâmetro, mas o código transpilado nos faz entender de forma muito mais clara.

O último detalhe importante a enfatizar sobre transpiladores é que eles devem ser pensados agora como uma parte padrão do ecossistema do processo de desenvolvimento. O JS irá continuar a evoluir, muito mais rápido do que antes, então a cada poucos meses novas sintaxes e novas funcionalidades serão adicionadas.

Se você usar um transpilador por padrão, você irá sempre poder fazer essa mudança para uma sintaxe mais nova sempre que você achá-la útil, ao invés de ter que ficar esperando por anos para os navegadores de hoje se tornarem obsoletos.

Existem alguns grandes transpiladores para escolher. Aqui algumas boas opções no momento da escrita dessa redação:

Não-JavaScript

Até aqui, a única coisa que cobrimos foi a linguagem do JS propriamente dito. A realidade é que grande parte do JS é escrito para rodar e interagir com ambientes como navegadores. Uma boa parte das coisas que você escreve no seu código são, estritamente falando, não diretamente controladas por JavaScript. Isso provavelmente soa um pouco estranho.

A forma mais comum de JavaScript não-JavaScript que você vai encontrar é o DOM API. Por exemplo:

var el = document.getElementById( "foo" );

A variável document existe como uma variável global quando seu código está rodando em um navegador. Ele não é provido pelo mecanismo do JS nem mesmo é particularmente controlado pela especificação do JavaScript. Ele tem a forma de algo que parece terrivelmente parecido com um object, ma não é exatamente assim. Ele é um object especial, muitas vezes chamado de "host object."

Além disso, o método getElementById(..) em document parece uma função normal em JS, mas é apenas uma pequena interface exposta a um método nativo provido pelo DOM através do seu navegador. Em alguns (nova geração) navegadores, essa camada pode também ser em JS, mas tradicionalmente o DOM e seus comportamentos são implementados em algo mais parecido com C/C++.

Um outro exemplo é com input/output (I/O).

O favorito de todos, alert(..), pipoca uma caixa de mensagem na janela do navegador do usuário. O alert(..) é provido para seu programa em JS pelo navegador, não pelo mecanismo do JS propriamente dito. O chamado que você faz envia uma mensagem para os mecanismos internos do navegador que fazem o desenho e enfim mostram a caixa de mensagem.

O mesmo acontece com o console.log(..); seu navegador provê os devidos mecanismos e colocam-os nas ferramentas do desenvolvedor.

Este livro, e toda esta série, focam na linguagem JavaScript. Esse é o motivo de você não ver nenhuma cobertura substancial desses mecanismos JavaScript não-JavaScript. Independentemente, você precisa estar atento à eles, visto que eles estarão em todo programa em JS que você for escrever!

Revisão

O primeiro passo para aprender o sabor JavaScript de programação é ter um entendimento básico dos mecanismos básicos como valores, tipos, clausuras de funções, this e prototipagem.

Claro, cada um desses tópicos merecem uma cobertura muito maior do que você viu aqui, mas é por isso que eles tem capítulos e livros dedicados para eles ao longo desta série de livros. Depois de se sentir confortável com os conceitos e exemplos de código deste capítulo, o resto da série te aguarda para que você se aprofunde na linguagem.

O capítulo final deste livro irá fazer um breve sumário de todos os outros títulos da série e outros conceitos que serão cobertos além do que já exploramos até aqui.

You Don't Know JS: Up & Going

Capítulo 3: Dentro do YDKJS

Sobre o que é essa série? Simplificando, trata-se de levar a sério a tarefa de aprender todas as partes do JavaScript, e não apenas um subconjunto da linguagem que alguns chamam de "as partes boas", e também não apenas o mínimo que você precisa fazer para terminar o seu trabalho.

Desenvolvedores sérios em outras linguagens esperam colocar seu esforço para aprender a maioria ou a totalidade da linguagem (ou linguagens) que eles mais usam, mas os desenvolvedores JavaScript parecem se destacar da multidão no sentido de normalmente não aprender muito da linguagem em si. Esta não é uma coisa boa, e não é algo que podemos continuar a permitir ser o modelo.

A série You Don't Know JS (YDKJS) está em contraste gritante com as abordagens típicas para aprender JS, e é diferente de praticamente qualquer outros livros sobre JS que você vai ler. Ele te desafia a sair da sua zona de conforto e de se perguntar os mais profundos "porquês" para cada comportamento que você encontrar. Você está pronto para esse desafio?

Vou usar este capítulo final para resumir o que esperar do resto dos livros da série, e como ir de forma mais eficaz sobre a construção de uma base de aprendizagem de JavaScript em cima da YDKJS.

Escopo & Encerramentos

Talvez uma das coisas mais fundamentais que você precisa aprender, é como o escopo de variáveis realmente funciona em JavaScript. Isso não é o suficiente para ter opiniões vagas sobre escopo.

O título Escopo & Encerramentos começa por desmascarar o equívoco comum que JS é uma "linguagem interpretada" e, portanto, não compilado. Não.

O motor de JS compila seu código logo antes (e às vezes durante!) a execução. Então, faremos uso de uma abordagem mais profunda do compilador para o nosso código, para entender como ele encontra e lida com declarações de variáveis e funções. Ao longo do caminho, vemos a típica metáfora para gerenciamento de escopo de variáveis em JS, "Hoisting" (Elevação).

É nesta compreensão crítica do "escopo léxico" que nós iremos basear a nossa exploração de Encerramento para o último capítulo do livro. Talvez Encerramento seja o conceito mais importante em toda a linguagem JavaScript, mas se você primeiramente não entender firmemente como o escopo funciona, Encerramento provavelmente permanecerá fora do seu alcance.

Uma aplicação importante de Encerramento é o module pattern, como nós introduzimos brevemente neste livro, no Capítulo 2. O module pattern é, talvez, o padrão de organização de código que mais prevalece em todos JavaScript; a profunda compreensão disso, deve ser uma de suas maiores prioridades.

this & Prototipagem de Objetos

Talvez uma das inverdades mais comuns e persistentes sobre JavaScript é que a palavra-chave this se refere à função qual ela aparece. Terrível engano.

A palavra-chave this é dinamicamente ligada com base em como a função em questão é executada, e existem quatro regras básicas para entender e determinar plenamente a ligação do this.

Intimamente relacionado com a palavra-chave this, está o mecanismo protótipo de objeto (object prototype), que é uma cadeia de pesquisa para as propriedades, similar ao modo léxico que o escopo de variáveis é encontrado. Mas envolto nos protótipos, está outro enorme erro sobre JS: a (falsa) ideia de emular classes e (a chamada "prototipagem") herança.

Infelizmente, o desejo de trazer o design pattern (padrão de projeto) de classes e heranças para o JavaScript é simplesmente a pior coisa que você poderia tentar fazer, porque enquanto a sintaxe pode induzí-lo a pensar que há algo como classes, de fato o mecanismo de protótipo é fundamentalmente oposto em o seu funcionamento.

O que está em questão é se é melhor ignorar a incompatibilidade e fingir que o que você está implementando é "herança", ou se é mais apropriado aprender e abraçar como o sistema de protótipo de objeto realmente funciona. Este último é mais apropriadamente chamado de "delegação de comportamento."

Então isso é mais sobre preferência sintática. A delegação é um sistema totalmente diferente, e mais poderoso, de design pattern, que substitui a necessidade de planejar classes e heranças. Mas essas afirmações com certeza vão pros ares frente à quase todos os outros post, livros, e conferências sobre o assunto para a duração da vida do JavaScript.

As afirmações que faço sobre delegação contra herança vem não de uma antipatia da linguagem e da sua sintaxe, mas a partir do desejo de ver a verdadeira capacidade da linguagem adequadamente aproveitada, e, a confusão sem fim e frustração indo embora.

Mas o caso que eu faço a respeito de protótipos e a delegação é muito mais do que o que eu vou falar aqui. Se você está pronto para reconsiderar tudo o que você pensa que sabe sobre as "classes" e "heranças" em JavaScript, eu lhe ofereço a oportunidade para "tomar a pílula vermelha" (Matrix 1999) e conferir os capítulos 4-6 do título this & Protótipo de Objetos desta série.

Tipos e Gramática

O terceiro título desta série tem foco principalmente em combater ainda outro tópico altamente controverso: a coerção de tipos. Provavelmente nenhum tópico causa mais frustração entre os desenvolvedores JS que quando você fala sobre as confusões acerca da coerção implícita.

De longe, a sabedoria popular diz que a coerção implícita é uma "parte ruim" da linguagem e deve ser evitada a qualquer custo. Na verdade, alguns tem ido tão longe ao ponto de chamá-la de falha na concepção da linguagem. Realmente, existem ferramentas que tem o foco em nada mais que escanear o códido e alertar se você estiver fazendo alguma coisa parecida com a coerção.

Mas a coerção é realmente confusa, tão ruim, tão falsa, que seu código estará condenado desde o início, se você usá-la?

Eu digo que não. Depois de ter consolidado uma compreensão de como tipos e valores realmente funcionam nos capítulos 1-3, o capítulo 4 assume este debate e explicará totalmente como a coerção funciona, com todos os detalhes. Nós veremos apenas que partes da coerção realmente surpreendem e quais partes fazem completo sentido se tiver tempo para aprender.

Mas eu não estou apenas sugerindo que a coerção é sensível e pode ser aprendida, eu estou seguro que a coerção é uma ferramenta incrivelmente útil e totalmente menosprezada que você deve usar em seu código. Eu estou dizendo que a coerção, quando usada corretamente, não apenas funciona, mas faz seu código ser melhor. Todos os pessimistas e céticos, certamente irão zombar disso, mas eu acredito que isso é uma das principais chaves para elevar seu conhecimento em JS.

Você apenas quer continuar seguindo o que a multidão diz, ou você está disposto à deixar todos os pressupostos de lado e olhar para a coerção com uma nova perspectiva? O volume Tipos & Gramática desta série irá te forçar a pensar.

Async & Performance

Os três primeiros títulos desta série tem o foco nos mecanismos centrais da linguagem, mas o quarto título ramifica-se levemente para cobrir padrões no topo da linguagem para gerenciar a programação assíncrona. Programação assíncrona não é somente crítica para a performance das nossas aplicações, ela está se tornando cada vez mais o fator crítico para a sua escrita e manutenção.

O livro começa primeiramente esclarecendo um monte de conceitos e terminologias confusas acerca de coisas como "assíncrino", "paralelismo" e "simultâneo", e explica detalhadamente como essas coisas se aplicam ou não ao JS.

Em seguida, passamos a examinar os callbacks como o principal método à possibilitar programação assíncrona. Mas é aqui que vemos rapidamente que o callback em si é insuficiente para as exigências modernas de programação assíncrona. Identificamos duas principais deficiências da codificação somente callback: a perda de confiança na Inversão de Controle (IoC), e falta de capacidade de raciocinar linearmente.

Para tratar dessas duas importantes deficiências, o ES6 introduz dois novos mecanismos (e, na verdade, padrões): promises e generators.

Promessas (promises) são um agregador independente, sobre um "valor futuro", que permite você pensar sobre ele e compô-los independentemente do valor estar pronto ou ainda não. Além disso, elas efetivamente resolvem os problemas de confiança na IoC roteando os callbacks através de um confiável e composto mecanismo de promise.

Os Geradores (Generators) introduzem um novo modo de execução para as funções JS, visto que o gerador pode ser pausado em pontos de yield e depois continuar de forma assíncrona. A capacidade de pausa-e-continua permite o síncrono, consequentemente, procura código no gerador para ser processado assincronamente nos bastidores. Ao fazer isso, nós abordamos as confusões não-linear, não-saltos-locais de callbacks, e assim, tornar nosso código assíncrono síncrono, procurando um modo de ser mais razoável.

Mas é essa combinação de promessas e geradores que "produz" nosso mais eficaz padrão de codificação assíncrona, até hoje, em JavaScript. De fato, muito do futuro da sofisticação assíncrona está por vir no ES7 e depois certamente será contruída sobre esse fundamento. Para ser sério sobre programação de modo eficaz em um mundo assíncrono, você deverá se acostumar com a combinação de promessas e geradores.

Se promessas e geradores representam a expressão de padrões que permitem nossos programas rodarem ao mesmo tempo, e assim, obter mais processamento realizado em um período menor, o JS tem muitas outras facetas de desempenho que vale a pena explorar.

O Capítulo 5 explora temas como o paralelismo de programa com o Web Workers e paralelismo de dados com SIMD, bem como técnicas de otimização de baixo nível, como ASM.js. O Capítulo 6 lança um olhar sobre otimização de desempenho do ponto de vista das técnicas de avaliação comparativas adequadas, incluindo que tipo de desempenho a se preocupar e qual ignorar.

Escrever JavaScript efetivamente significa escrever código que pode quebrar as barreiras de restrição do que está sendo executado de forma dinâmica em uma ampla gama de navegadores e outros ambientes. Isso exige muito esforço e um planejamento complexo e detalhado da nossa parte, para levar um programa de "isso funciona" para "isso funciona bem".

O título Assíncrono & Desempenho (Async & Performance) é feito para te dar todas as ferramentas e habilidades que você precisa para escrever código JavaScript sensato e performático.

ES6 & Além

Não importa o quanto você se sinta o mestre em JavaScript até aqui, a verdade é que JavaScript nunca deixará de evoluir, e além disso, a taxa de evolução está aumentando rapidamente. Este fato é quase uma metáfora para o espírito desta série, compreender que nós nunca vamos saber tudo sobre JS, porque tão logo você dominar tudo, surgirão coisas novas que irão para a fila do que você precisa aprender.

Este título é dedicado tanto para visões de curto e médio prazo de para onde a linguagem caminha, não apenas as coisas conhecidas como ES6 mas as também as coisas prováveis que estão por vir.

Enquanto todos os títulos desta série compreendem o estado do JavaScript do momento em que foram escritos, que está na metade do caminho para a adoção do ES6, o foco primário desta série tem sido mais no ES5. Agora, nós queremos voltar nossa atenção para o ES6, ES7, e...

Já que o ES6 está quase completo, no momento da redação deste texto, ES6 & Além começa dividindo o matérial sólido da paisagem do ES6 em diversas categorias chave, incluindo a nova sintaxe, as novas (coleções de) estruturas de dados, e a nova capacidade de processamento e APIs. Nós cobriremos cada uma dessas novas características do ES6, em diferentes níveis de detalhe, incluindo a revisão de detalhes que já foram citados em outros livros desta série.

Algumas coisas interessantes do ES6 para acompanhar e ler sobre: desestruturação, parâmetros com valores padrão, símbolos, métodos concisos, propriedades calculadas, arrow functions, bloco de escopo, promessas, geradores, iterators, módulos, proxies, weakmaps, e mais, muito mais! Ufa, o ES6 tem muito poder!

A primeira parte do livro é um roteiro para todas as coisas que você precisa aprender para se preparar para o novo e melhorado JavaScript que você vai escrever e explorar pelos próximos anos.

A última parte do livro volta e foca rapidamente nas coisas que nós provavelmente podemos esperar ver no futuro do JavaScript. A realização mais importante aqui é o pós-ES6, o JS provavelmente vai evoluir funcionalidade por funcionalidade a cada versão, o que significa que podemos esperar para ver coisas num futuro próximo vindo mais cedo do que você pode imaginar.

O futuro para o JavaScript é brilhante. Não é a hora de começarmos a aprendê-lo!?

Revisão

A série YDKJS é dedicada à questão de que todos os desenvolvedores JS podem e devem aprender todas as partes desta grandiosa linguagem. Nem a opinião de alguém, nem framework's, nem prazos de projetos devem ser desculpa para você deixar de aprender e compreender JavaScript profundamente.

Nós pegamos cada área importante da linguagem, focamos e dedicamos um livro curto mas denso para explorar plenamente todas as partes que você talvez pensou que sabia, mas não provavelmente em sua totalidade.

"You Don't Know JS" ("Você Não Sabe JS") não é uma crítica ou um insulto. Isso é uma percepção que todos nós, inclusive eu, devemos ter. Aprender JavaScript não é um objetivo final, mas um processo. Nós não sabemos JavaScript ainda, mas vamos!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment