JS
Decolando no
JavaScript
Prepare-se para o React e Node.
Aprenda ES6, ES7 & ES8+.
Introdução
Aprenda tudo o que você precisa saber sobre
JavaScript pra começar os seus estudos em
um framework front-end, mais
especificamente, no React e também no
Node.js. Aprenderemos funcionalidades
introduzidas nas suas especificações mais
modernas (ES6, ES7, ES8+), portanto, o
conteúdo que você lerá nas próximas páginas
está 100% atualizado e coerente com os dias
atuais.
Introdução
Começamos
por aqui...
Antes de qualquer coisa, eu quero agradecer
você, leitor, por você ter depositado a sua
confiança em mim e por ter feito o download
deste material. Pode ter certeza de que você
não vai se arrepender, pois eu dediquei muitas
horas do meu tempo pra preparar o melhor
conteúdo possível pra você.
Começamos por aqui...
Sobre o
Autor
Me chamo Felipe Rocha, tenho 20 anos e programo desde os meus 11.
Sou apaixonado por tecnologia e sou simplesmente fascinado pelo
poder que a programação me dá. Com ela eu consigo colocar as minhas
ideias em prática – convertê-las em um produto real e eventualmente,
em um negócio – e criar produtos que podem impactar a vida das
pessoas.
Acredito que a propagação de conhecimento é um dos mecanismos
mais transformadores da nossa sociedade, e é por isso que amo fazer o
que estou fazendo hoje, neste material, e o que faço diariamente nas
redes sociais – compartilhar o meu conhecimento e ajudar as pessoas
com ele. Hoje, eu vou ajudar você.
Sobre o Autor
Eu (também)
passei por
dificuldades...
Antes de qualquer coisa, eu quero dizer para você que no início eu passei por
muitas dificuldades, principalmente para aprender o tema que vou ensinar para
você neste material. Portanto, não se desespere caso você fique com
dificuldades para entender algum assunto – isso é completamente normal.
Apenas foque em não desistir, e continuar estudando mesmo quando a
desmotivação bater na porta. Para ajudar você nessa, vou deixar o meu
Instagram e o meu canal no YouTube aqui embaixo, para caso você precise de
ajuda.
Eu (também) passei por dificuldades...
Bora lá?
Agora que você já me conhece melhor,
vamos começar a nossa jornada.
Vamos para o conteúdo!
O que vamos
abordar
Constantes e Variáveis de Escopo (const & let)
Const vs. let
Template Strings
Arrow Functions
Destructuring
Promises
O que vamos abordar
Constantes
e Variáveis
de Escopo
(const & let)
Constantes e Variáveis de Escopo (const & let)
Antes do ES6, só havia uma maneira de declarar uma variável no JavaScript:
utilizando a keyword var.
E o grande calcanhar de aquiles dessas variáveis declaradas com o var era que
elas não possuíam block scope. Para entender o que isso quer dizer, dê uma
olhada no exemplo abaixo:
Esse comportamento é no mínimo estranho, concorda? Declaramos a variável
message dentro do bloco de código do if e, mesmo assim, conseguimos
acessá-la fora dele. Como você pode imaginar, esse não é o ideal na maioria
das situações, afinal, se declaramos uma variável dentro de um if,
provavelmente não queremos que ela fique acessível fora dele. Esse tipo de
situação acontece por conta da ausência do block scoping.
Constantes e Variáveis de Escopo (const & let) 07
E as keywords const e let vieram justamente para resolver esse problema. Elas
possuem block scoping e, portanto, só são acessíveis dentro do bloco onde
foram declaradas.
Replicando o mesmo exemplo anterior, mas agora usando const, teremos o
seguinte resultado:
Agora faz sentido! Declaramos a variável message dentro de um bloco if,
portanto, ela só é acessível dentro dele. Sensacional!
Constantes e Variáveis de Escopo (const & let) 08
const
vs. let
Tanto const quanto let possuem block scoping. A diferença entre as duas é
que as variáveis declaradas com const não podem ser reatribuídas, já as com
let, podem.
Beleza, mas qual
devo usar?
Opte sempre por utilizar const e/ou let. As variáveis declaradas com var, por
não possuírem block scoping, podem causar muita confusão no código,
principalmente em aplicações maiores.
cont vs. let 10
Template
Strings
No ES6, foram introduzidas as template strings. Elas proporcionam uma forma
extremamente mais limpa de adicionar expressões em strings. Dê uma olhada
neste exemplo:
Viu como é mais fácil? E para usá-las, basta criar a string com acento grave (`)
e colocar as expressões em volta de um cifrão e duas chaves (${}).
Template Strings 12
Arrow
Functions
As arrow functions foram introduzidas no ES6. Elas possuem uma sintaxe
mais curta e simples quando comparadas às funções tradicionais:
Também conseguimos dar um return implícito quando a escrevemos da
seguinte forma:
Arrow Functions 14
Ambas as funções acima geram o mesmo resultado: elas retornam a soma da
variável a com a b. Sensacional!
Beleza... a sintaxe é excelente, mas o que realmente torna uma arrow function
tão vantajosa é o seu this. Antes de eu explicá-lo para você, vamos entender
como ele funciona em uma função convencional.
Arrow Functions 15
Entendendo o this em uma
função convencional
O this em uma função é definido no lugar em que ela é chamada. Por exemplo:
Podemos ver que, ao invés de considerar o this do lugar onde foi criada —
dentro do método printNameFunction, cujo o this é o da classe Person — ela
considerou o do lugar onde foi chamada. Portanto, o this.name é igual a Doe
John, como definimos na primeira linha do código, e não John Doe.
Arrow Functions 16
Esse comportamento costuma causar muitos problemas e pode ser, muitas
vezes, confuso para o desenvolvedor. Em outras linguagens de programação,
como Java e C#, o this.name da função printNameFunction seria igual a John
Doe — o que é o esperado.
Entendendo o this em uma
Arrow Function
O this em uma arrow function, diferentemente do this em uma função
convencional, é definido no lugar onde ela foi criada, e não onde foi
executada. Por exemplo:
Arrow Functions 17
Isso é, simplesmente, sensacional. Conseguimos ter um controle muito maior
do valor do this da nossa função. Nas arrow functions, ele se comporta como
deveria — é herdado do contexto onde a função foi criada.
Beleza, mas qual
devo usar?
Para responder essa pergunta, eu vou mostrar para você um caso onde as
arrow functions podem não ser a melhor opção: na criação de métodos de
objetos.
Dê uma olhada nesse exemplo:
Arrow Functions 18
A propriedade sings executa uma arrow function. Nela, o this.name é
undefined, porque, como já aprendemos, as arrow functions herdam o this do
contexto ondem foram criadas. Neste exemplo, o contexto em que foram
criadas é o mesmo ao qual a variável person pertence — o contexto global. No
navegador, por exemplo, ele seria o window. Se definíssemos o name fora do
objeto — no contexto global —, por exemplo, teríamos este resultado:
Agora, o this.name é igual a Doe John porque o definimos no contexto onde a
arrow function foi criada — neste caso, no global.
Portanto, em situações como essa, pode não ser uma boa ideia utilizar as
arrow functions. Mas, nas demais, ela provavelmente será a melhor opção.
Arrow Functions 19
Destruc-
turing
Destructuring, também conhecido como desestruturação, é uma forma
extremamente simples de acessar as propriedades de um objeto e/ou valores
de uma lista, que foi introduzida no ES6.
Utilizando o destructuring em
listas (arrays)
Imagine que temos uma lista de números, e que queremos criar três variáveis
para cada um dos três primeiro elementos dessa lista. Como faríamos isso?
Não parece um código muito legal, concorda? Imagina se adicionássemos
mais cinco números na lista e que quiséssemos também guardar cada um
deles em uma variável... teríamos que criar mais cinco linhas de código, o que
não é o ideal.
Destructuring 21
Mas, uma boa notícia! O destructuring consegue resolver essa situação de
uma forma extremamente mais limpa:
Sensacional, concorda? Nosso código ficou muito mais enxuto e, na minha
opinião, mais legível também.
Além disso, conseguimos interagir com o resto de listas, e usá-los até mesmo
na atribuição de variáveis. Se isso soou confuso, fica tranquilo(a) hahaha! Dê
uma olhada nos exemplos:
Destructuring 22
Pegamos o 10 e o 20 e os colocamos nas variáveis a e b, respectivamente.
Depois, atribuímos o resto da lista a variável rest. Quando quisermos pegar o
restante de uma lista, utilizamos os três pontos (...), como fizemos acima.
Podemos fazer mais algumas mágicas com esses três pontinhos hahaha! Dá
uma olhada:
Sensa-
cional!
E pra fechar, também conseguimos inverter os valores de variáveis:
Destructuring 23
Utilizando o destructuring
em objetos
Imagine que temos um objeto user, com algumas propriedades, como nome,
idade e email, e que queremos acessar todas essas propriedades. Como
faríamos isso?
Destructuring 24
Mais uma vez, isso não parece ser uma forma muito ideal de acessá-las,
concorda?
Imagine que tivéssemos mais dez propriedades dentro do user e que
quiséssemos acessar todas elas. Escrever um "user." pra cada uma delas
deixará o nosso código bem repetitivo.
Vejamos como podemos fazer isso — de uma forma extremamente melhor —
utilizando destructuring:
Destructuring 25
Simplesmente sensacional! Retiramos aqueles redundantes "user." do nosso
código, e agora conseguimos acessar as propriedades do user mais
facilmente.
Mas calma, não é só isso! Vamos supor que eu queira, por algum motivo,
mudar o nome da variável que guarda o name do user, mas não alterar o nome
da propriedade em si. Posso fazer isso da seguinte forma:
Destructuring 26
Agora, posso me referir ao user.name por meio da variável fullName.
Simplesmente incrível!
E, claro, também conseguimos, assim como nas listas, interagir com o resto de
um objeto:
Destructuring 27
Agora, posso me referir ao user.name por meio da variável fullName.
Simplesmente incrível!
E, claro, também conseguimos, assim como nas listas, interagir com o resto de
um objeto:
Fazendo isso, conseguimos nos prevenir de acessarmos uma propriedade
inexistente em um objeto. No exemplo acima, o b não existe no objeto no qual
estamos utilizando destructuring, mas, mesmo assim, conseguimos atribuir
um valor para ele. Mas é importante ressaltar que o valor original não será
alterado; ele continuará sendo { a: 3 }.
E por último, mas não menos importante, podemos também usar o
destructuring em propriedades nestadas:
Destructuring 28
Simplesmente
Sensacional!!!
Destructuring 29
Promises
Antes de entender o que e para que servem as Promises, precisamos
compreender o funcionamento do JavaScript como um todo.
O JavaScript só executa uma
coisa de cada vez
Quando executamos uma função, ela é enviada para um lugar chamado call
stack, o qual consegue executar apenas uma por vez. Vamos exemplificar:
Promises 31
Vamos por partes:
Quando executamos uma função, ela é enviada para um lugar chamado call
stack, o qual consegue executar apenas uma por vez. Vamos exemplificar:
1. Quando rodarmos esse código, a primeira coisa que o JavaScript vai
executar, obviamente, é a linha 1. Portanto, ele assinalará uma função à variável
printName.
2. Depois, na linha 5, ele vai assinalar uma função à variável printAge.
3. E, finalmente, na linha 9, ele vai executar a função printName. Essa função
será enviada para o call stack, que executará ela normalmente e logará "John
Doe" no console. Após isso, o call stack estará vazio.
4. Depois, na linha 10, vamos executar a função printAge, que será enviada
para o call stack — assim como foi a printName — e será executada. "21" será
logado no console.
No fluxo acima podemos facilmente visualizar que o JavaScript não executa
mais de uma função por vez. O call stack recebe uma, a executa e, só após
executá-la, ele recebe a próxima.
Promises 32
Mas e quando uma função demora
muito para ser executada?
Vamos supor que enviamos para o call stack uma função que faz uma
requisição para uma API. Imagine que essa API esteja muito lenta, e que leve
10 segundos para retornar algo para nós. Consequentemente, essa função
levará 10 segundos para completar sua execução. O que acontecerá com o
call stack? Ele simplesmente ficará congelado até que essa função termine de
ser executada, ou seja, ele ficará parado por 10 segundos. Relembrando: ele
só executa uma coisa de cada vez.
Mas, uma boa noticia: as Promises vieram resolver justamente este tipo de
problema! Imagine o seguinte código:
Promises 33
Ele é bem similar ao que vimos anteriormente, mas com uma diferença: a
função printName vai levar 5 segundos para ser executada, porque
colocamos um setTimeout dentro dela.
O que vai acontecer, neste caso? Bom, o call stack executará a função
printName e, consequentemente, ficará parado por 5 segundos. A nossa
aplicação ficará totalmente congelada e a printAge só será executada após
esse período. Porque, mais uma vez, o call stack só executa uma coisa de
cada vez.
Promises 34
Resolvendo isso com
Promises
Com as Promises, podemos enviar uma função para o call stack que não o
congelará, mesmo que ela leve, como no exemplo acima, 5 segundos para
completar a sua execução.
Vamos reproduzir o mesmo exemplo anterior, mas agora utilizando-as:
Não se desespere! Vamos juntos entender como funciona a sintaxe de uma
Promise:
Promises 35
A primeira coisa que fazemos é retornar uma nova Promise, como fizemos na
linha 2. Ela recebe dois parâmetros: resolve e reject. O resolve é chamado
quando tudo ocorre bem, e nenhum erro é apresentado. Já o reject é
executado quando algo de errado acontece.
Agora, quando executamos a função returnName, na linha 17, ela "fala" o
seguinte para o call stack: olha, me deixe de lado por enquanto, e siga
executando as próximas funções que estão na fila (neste caso, a printAge) que
eu prometo que, daqui a pouco, eu vou te retornar alguma coisa. E a mágica
acontece aqui, pois, mesmo que a returnName demore 5 segundos para
completar a sua execução, o call stack pegará a próxima função (printName) e
a executará normalmente, sem esperar por estes 5 segundos.
E então, por meio do .then, podemos passar uma função que será executada
após a Promise ter sido resolvida (por meio do resolve). Ela recebe como
parâmetro o valor que foi passado no resolve, na linha 5. Neste caso, "John
Doe" será logado no console.
Promises 36
Perceba que o "21" foi logado antes do "John Doe", mesmo com a função
returnName tendo sido chamada antes da printAge. Isso é uma
demonstração perfeita das Promises em ação!
Obs.: podemos passar uma função para o .catch da mesma maneira que
passamos para o .then. Ela será executada quando a Promise for rejeitada
(por meio do reject), e receberá como parâmetro o valor que passarmos para o
reject.
Promises com
async/await
Ao invés de usarmos a sintaxe que vimos acima para lidar com as Promises
(.then e .catch) podemos utilizar uma muito melhor e mais limpa, que foi
introduzida no ES2017: o async await. Vamos vê-la na prática em um exemplo
real: vamos chamar uma API utilizando-a!
Mas antes, vamos fazer este processo de chamada de um API com a sintaxe
que já conhecemos, .then e .catch.
Promises 37
Neste exemplo, estamos utilizando uma função nativa do JavaScript, chamada
fetch. Com ela, podemos fazer requisições para APIs. Neste caso, fizemos para
uma que retornará uma lista de usuários.
O fetch retorna uma Promise, com a response (resposta) da requisição em
seu resolve e o erro (caso ocorra) em seu reject. Na linha 3, lidamos
justamente com ela, convertendo-a para JSON utilizando o método .json, que
também é nativo do JavaScript. Este método retorna uma Promise, com o valor
no qual o invocamos convertido para JSON em seu resolve. Na linha 4,
recebemos este valor convertido e o logamos no console.
Dê um ZOOM para
O resultado será o seguinte:
enxergar melhor
Funcionou que é uma beleza! Mas esse código pode melhorar bastante se
usarmos o async/await. Portanto, vamos convertê-lo:
Promises 38
A primeira coisa que fizemos, na linha 1, foi adicionar a keyword async ao início
da nossa função, marcando-a, assim, como assíncrona. Precisamos fazer isso
pois só podemos usar o async/await em funções assíncronas.
Agora, a mágica começa na linha 2. Veja que, ao invés de usar .then para
acessar a response do fetch (que é uma Promise), simplesmente
adicionamos await antes dele. Agora, o valor do seu resolve será guardado na
variável response.
Na linha 3, vamos converter a response para JSON — como fizemos
anteriormente. Para isso, vamos utilizar, novamente, a keyword await antes de
chamar o método .json (que é uma Promise) e, mais uma vez, o valor do seu
resolve (valor convertido para JSON) será armazenado na variável
jsonResponse, que será, na linha 5, logada no console:
Dê um ZOOM para enxergar melhor
Ou seja, com o async/await conseguimos armazenar o resolve de uma
Promise dentro de uma variável, e eliminar a utilização do .then! Simplesmente
sensacional, não acha?
Promises 39
Beleza, mas e o .catch?
Em caso de erro em uma Promise, podemos lidar com o seu reject por meio do
.catch. Beleza! Disso você já sabe muito bem. Mas, no async/await, podemos
lidar com ele de uma forma mais limpa. Simplesmente colocamos todas as
nossas chamadas de Promises (await) dentro de um bloco try, catch:
Agora, se alguma das Promises (fetch ou .json) falharem, o nosso bloco catch
será executado. Portanto, o erro (reject) será logado no console.
Bônus: map & filter
Como uma forma de recompensar você, leitor(a), por ter ficado até aqui, vou
lhe apresentar dois métodos importantíssimos, e que são muito utilizados em
frameworks front-end, especialmente no React: o map e o filter.
Ambos são úteis na hora de trabalhar com manipulação de listas (arrays).
Vamos começar pelo map, e depois vamos para o filter.
Promises 40
Map
A função map executa uma função em cada elemento de um array e retorna
um novo array com estes elementos, tenha sido eles modificados ou não por
ela. Vamos vê-lo na prática:
Veja que executamos a função "(number) number *2" para cada elemento do
array numbers, ou seja, multiplicamos cada um por 2. O resultado final foi
"[2, 4, 6]". Vale lembrar que o que será adicionado no array gerado pelo map é
o que é retornado na função que passamos para ele. No exemplo acima,
retornamos "number *2".
Vamos para mais um exemplo, mas agora, utilizando o map em um array de
objetos:
Promises 41
Veja que para cada elemento do array users, retornamos um objeto com o
elemento (user) e com a sua idade (age) multiplicada por 2.
Filter
Assim como o map, o filter executa uma função para cada elemento de um
array e retorna um novo, mas a principal diferença dele é a seguinte: no array
gerado por ele, só são adicionados os itens no qual a função recebida por ele
retornou true.
Isso pareceu bem confuso, não é? Mas vamos vê-lo na prática, que fica mais
fácil:
Promises 42
Para cada elemento do array numbers, verificamos se o resto da sua divisão
por 2 é 0 (number / 2 === 0), ou seja, se ele é par. Se for, true será retornado
e, portanto, ele será adicionado ao novo array gerado pelo filter. Se não, false
será retornando, e ele não será adicionado.
No final, o array gerado pelo filter, o evenNumbers, terá apenas o 2 e 4, pois
apenas eles, dentre todos os elementos do array numbers, são pares.
Vamos para mais um exemplo, mas agora, utilizando o filter em um array de
objetos:
Promises 43
Para cada elemento do array users, verificamos se ele tem a idade (age) igual a
20. Se tiver, true será retornando, e ele será adicionado ao novo array gerado
pelo filter, usersWithAge20. Se não, false será retornando, e ele não será
adicionado.
Minha dica de
ouro para você
fixar este
conteúdo
Sei que o que você viu aqui pode parecer assustador – embora eu tenha dado
o meu melhor para tornar o mais simples para você – mas fique tranquilo, isso é
totalmente normal. Como falei para você no início, eu já me senti assim várias
vezes.
A dica de ouro que eu dou para você aprender de verdade tudo que você viu
aqui é: foque em entender um tema de cada vez, e pratique-o no processo.
Não tente focar em todos de uma só vez. Dê pequenos passos.
Se você precisar de ajuda, estou aqui para você. Basta me mandar uma
mensagem no nosso Instagram, @dicasparadevs, beleza? :)
Promises 44
Fim da Linha
Chegamos ao final, pelo menos por aqui, porque agora é com você. Gostaria
de parabenizá-lo novamente por ter escolhido este material e por ter lido-o até
aqui, você deu um grande passo e estou orgulhoso de você.
Escrevi cada linha deste e-book com muito carinho e, especialmente,
priorizando você e sua aprendizagem. Espero que tudo tenha ficado claro e
que você tenha gostado do que leu.
E por último, mas não menos importante, quero dizer para você que você não
está sozinho nessa. Temos uma comunidade incrível em nosso Instagram,
@byfeliperocha, onde há muitos(as) programadores(as) incríveis que estão
buscando conhecimento, assim como você. Se você tiver alguma dúvida,
como já citei acima, você pode ficar à vontade para me mandar uma
mensagem lá!
Obrigado!
Fim da Linha