Nessa aula visitaremos os conceitos básicos, como:
- Uso do console.log
- Interação via browser (alert, prompt e confirm)
- Seletores JS (por tags HTML, classes, ids e atributos)
- Tipos de Dados (boolean, null, undefined, number, bitInt, string, symbol e objects)
- Condicionais (if, else, else if, switch)
- Loops (for, forEach, for in, for of, while, do while)
- Escopos (global, local e bloco)
- Variáveis (const, var e let)
Basicamente trazemos informações para o DevTools através do console.log. Como o próprio nome já diz, estamos acessando o console e criando um log. O log é um método, uma função - log('conteúdo que queremos exibir'). Faremos uma série de práticas para entendermos o console.log.
Sabia que pode incluir estilo às informações inseridas no console? É um recuro um pouco limitado, mas nos ajuda a diferenciar as informações que estamos incluindo no log do console. Para isso basta incluirmos %c antes de onde deve ser aplicado cada estilo e, na sequência, passar esses estilos como um segundo parâmetro (tendo como valor a mesma sintaxe do CSS). Exemplo:
console.log(
'Text normal, %ctexto com fundo preto e cor branca, %ctexto com fundo amarelo e cor azul',
'background-color:black;color:white;',
'background-color:#ffff00;color:blue;'
);
Podemos incluir variáveis e código JS dentro da expressão sem termos de concatenar com +. Para isso usamos ${ variáveis, etc. }. Para que isso seja possível, devemos usar crases ao invés de aspas simples. Exemplo:
nome = 'Fulano';
// Usando concatenação
console.log('O valor de nome é: ' + nome)
// Usando Interpolação
console.log(`O valor de nome é: ${nome}`)
Template strings são strings com valores embutidos. A interpolação é uma delas. Algumas outras são:
// Quebra de linha:
console.log('Linha 1\nLinha 2\nLinha 3')
// Tabulação:
console.log('Linha 1, Coluna 1\n\tLinha 2, Coluna 2\n\t\tLinha 3, Coluna 3\n\tLinha 4, Coluna 2\nLinha 5, Coluna 1')
Temos ainda outras funções nativas referentes ao console, como:
Traz o log só que amarelo e com o ícone de alerta (se não quiser setar 'na mão' o estilo com o %c)
Traz o log só que vermelho (e com o ícone X)
Nos traz o 'rastro' do que está acontecendo, indicando o arquivo e linha de cada item chamado. Exemplo:
function x( param ) {
function y( param ) {
console.trace( `${param} (usando trace)`);
console.log( `${param} (usando log)`);
}
y( param + 1 );
}
x( 6 );
// Experimente rodar esse trecho de código (snippet) no console e veja a diferença
Também podemos usar o console.table para mostrar as chaves e valores (propriedades) de um objeto ou array. Veja:
alunos = [
{ nome : "Fulano", idade : 10 },
{ nome : "Ciclano", idade : 20 },
{ nome : "Beltrano", idade : 30 },
]
console.table(alunos)
Podemos ainda medir o tempo que demora entre um ponto e outro do nosso script. Basta declararmos console.time() e console.timeEnd(), desse modo:
console.time('tempo do loop')
arr = []
for ( let i = 0; i < 1000; i++ ) {
arr.push(i)
}
console.timeEnd('tempo do loop')
Retorna uma mensagem caso a condição não seja verdadeira. Para não precisarmos criar um if para validar coisas simples
minhaVar = 10
console.assert(minhaVar < 5, 'minhaVar não é menor que 5')
Temos 3 maneiras de interagir com o usuário através do próprio browser. Essas interações consistem naquelas 'caixinhas' (tipo pop up). Essas 3 maneiras são:
Simplesmente exibe uma mensagem para o usuário (exemplo: 'Bem vindo!')
Solicita uma confirmação do usuário (exemplo: 'Tenho mais de 18 anos') Podemos guardar a confirmação numa variável booleana
Permite que o usuário digite algo na 'caixinha' (exemplo: 'Digite seu nome') Podemos guardar o valor inserido numa variável
Funcionam de modo bem semelhante ao CSS, porém precisamos acessar o documento (document) para então selecionarmos os elementos. De forma geral, podemos fazer isso de duas maneiras:
Selecionamos o elemento especificando a forma de seleção. Temos as seguintes opções:
Usamos o comando document.getElementsByTagName(tag). Note que estamos usando Elements no plural. Ou seja, receberemos uma Collection (coleção) de todos os elementos que se enquadrem nessa coleção.
paragrafos = document.getElementByTagName('p');
Usamos o comando document.getElementsByClassName(classe). Novamente estamos usando Elements no plural. Ou seja, receberemos uma Collection (coleção) de todos os elementos que se enquadrem nessa coleção.
elementosVerdes = document.getElementByClassName('elemento-verde');
Usamos o comando document.getElementById(id). Nesse caso, considerando o uso correto do ID, só teremos um único elemento com o ID buscado. Logo, receberemos um único elemento como retorno. Vale ressaltar que, caso haja mais de um elemento com o mesmo ID, receberemos o primeiro deles (conforme ordem do próprio documento).
formulario = document.getElementByTagName('meuForm');
Selecionamos o elemento especificando o tipo de seleção no próprio termo a ser buscado (como no CSS). Temos as seguintes opções:
Usamos o comando document.querySelector(.classe), document.querySelector(#id), document.querySelector(tag) ou document.querySelector([nomeAtributo="valorAtributo"]). Esse seletor retorna apenas um resultado, então - idealmente - deveria ser utilizado apenas para IDs, para elementos únicos.
paragrafoPrincipal = document.querySelector('#paragrafoPrincipal');
Usamos o comando document.querySelectorAll(.classe), document.querySelectorAll(#id), document.querySelectorAll(tag) ou document.querySelectorAll([nomeAtributo="valorAtributo"]). Esse seletor já retorna uma coleção, então mesmo que só haja um elemento na coleção, deve ser tratado como tal.
inputsNumber = document.querySelectorAll('[type="number"]');
Coleções | Usamos o índice dos elementos dentro da coleção para acessá-los, como num array. Então se quisermos acessar a primeira ocorrência, devemos usar o índice [0].
Temos 2 divisões macro - os dados primitivos e objetos. À grosso modo, os tipos primitivos ficam guardados numa stack (pilha), na memória física. Já os objetos ficam guardados no heap (um amontoado) - e esses objetos são referenciados por ponteiros guardados na stack. Vale pontuar que quando criamos objetos que sejam cópia de outros objetos, na realidade estamos copiando o pointer (ponteiro, ou seja, o referenciador), e não o objeto em si.
Temos os seguintes dados primitivos:
Tem duas opções de valor - true ou false
Tem seu valor setado como null, ou seja, é nulo (não vazio)
Diferente do null, ele ainda não recebeu um valor específico
Assim como no PHP, a string é um conjunto de caracteres (ou pode ser vazia também)
É um número, que pode variar de -2 elevado à potência 53 -1 até 2 elevado à potência 53 - 1. Aceita os mesmos operadores vistos em PHP (+, -, *, /, %) Também temos o Infinity, o -Infinity (ambos se comportam como Infinito, positivo e negativo). E o NaN (Not a Number), que é retornado quando tentamos realizar operações matemáticas com dados que não sejam Number. Observação: para números que ultrapassam o limite do number temos o BigInt.
Representa um valor numérico do objeto instanciado. Pode ser uma string ou um número inteiro. Não pode ser usado com métodos Math. Extrapola os limites do number (permite números maiores), porém perde a precisão (exemplo: 5n / 2n retorna 2n, e não 2.5n).
De forma superficial, serve para gerarmos propriedades/chaves de objetos anônimos. Não é enumerável e tem uso bem específico e limitado. Não entraremos nesse tema no curso, mas deixamos um link para maiores informações ao final desse documento.
São os objetos. Verão que todos os tipos de dado declarados abaixo são, na realidade, objetos. Os objetos possuem métodos e propriedades (assim como vimos na aula de POO, no entanto, ao invés da single arrow usada em PHP - -> - usamos um ponto para acesá-los em JS). Os principais são:
São arrays iguais aos de PHP, com valores discriminados através de seus índices: meuArray = ["x", 23, "cachorro"].
Funcionam como arrays associativos, onde temos pares de chaves (strings) e valores (de quaisquer tipos). São representados com as chaves:
meuObj = {
"propriedade1" : "valor",
"propriedade2" : 123456
}
Função - ou function - é considerada como um Objeto Fundamental em JS (assim como Objetos e Erros).
Truque: podemos declarar uma função da maneira tradicional (minhaFuncao( ) { ... }) ou podemos usar a arrow function, das seguintes formas:
// Arrow Function sem parâmetros
function = ( ) => {
// ...
}
// Arrow Function com UM parâmetro
function = paramUnico => {
// ...
}
// Arrow Function com DOIS (ou mais) parâmetros
function = ( paramUm, paramDois ) => {
//...
}
// Arrow Function com UM parâmetro e apenas um return direto (dispensa chaves)
function = x => console.log(x)
A única desvantagem da arrow function é que ela não permite usarmos a palavra mágica this (que se refere ao elemento em questão dentro da função).
Temos o utilitário Date já construído internamente no JS.
Trata-se do nosso querido e já conhecido JSON, muito utilizado no consumo de APIs.
Também temos outros tipos de objetos como: Map, Set, WeakMap, WeakSet (Maps associam valores a objetos enquanto Sets são conjuntos de objetos).
Funcionam exatamente como visto em PHP. Temos as condicionais:
Onde, caso a condição declarada seja verdadeira, o código é executado.
Caso a condição seja verdadeira, o primeiro bloco de código é executado. Caso contrário (else, senão), o segundo bloco é adicionado.
Podemos incluir novas condições com o else if.
Truques
Podemos simplificar o if / else com a seguinte sintaxe:
condição
? caso true
: caso false
// ou em uma linha apenas
condição ? caso true : caso false
Podemos simplificar o if com a seguinte sintaxe:
condição
&& caso true
// ou em uma linha apenas
condição && caso true
Determinamos um parâmetro e então criamos n casos para determinarmos o comportamento do nosso código. Curiosidade: podemos ter true como parâmetro e, em cada caso declaramos comparações - caso a comparação seja verdadeira, o respectivo bloco de ações será executado
Também funcionam de maneira bem semelhante ao PHP (com poucas diferenças). Também temos 2 loops novos, descritos mais adiante.
Aquele loop mais básico, onde declaramos um valor inicial, uma condição para o loop continuar 'rodando' e um incremento:
for ( let i = 0; i < arr.length; i++) {
// ...
}
A lógica é a mesma do PHP, mas a sintaxe é distinta. Aplicamos o método forEach a uma coleção e definimos o que deve ser feito dentro de uma função declarada como parâmetro:
arr.forEach ( function() {
// ...
})
Assim como no PHP, declaramos uma condição - enquanto for verdadeira, o loop 'roda' (lembre-se de definir o incremento para não cair num loop infinito):
while (condicao) {
// ...
}
Também igual ao loop no PHP: declaramos a execução e então a condição - o código é executado e então a condição é verificada.
do {
// ...
} while (condicao)
Novos Loops!
Lembra o foreach do PHP. Usamos para percorrermos coleções enumeráveis, como arrays. Caminha sobre os índices do array. Então temos a unidade e a coleção:
for ( unidade of colecao ) {
// ...
}
Funciona igual ao for of, mas é aplicado a objetos, cujas propriedades não são enumeráveis. Caminha sobre as propriedades de um objeto:
for ( propriedade in objeto ) {
// ...
}
É muito importante entenderem como funcionam os escopos para que entendam como criar as variáveis e como e onde acessá-las. Basicamente temos 3 tipos de escopo:
Trata-se do script ou documento como um todo. Variáveis de escopo global podem ser acessadas dentro de qualquer escopo, em qualquer parte do documento.
Chamamos de escopo local quando criamos uma variável dentro de uma função. Alguns uma variável pode ou não transcender/extrapolar esse escopo. Precisamos entender esses conceitos para que isso ocorra propositadamente, e não acidentalmente.
Chamamos de bloco os blocos de condicionais ou loops. Muitas vezes queremos manter nossas variáveis dentro daquele bloco - principalmente quando estamos iterando objetos. Imagine um script onde temos dois loops. Em ambos estamos usando o for e usando i para iteração (i++). Se não mantivermos o escopo de cada i dentro do seu respectivo bloco, um loop passará a interferir no outro - normalmente esse tipo de comportamento não é desejado.
Escopo | Aplicação |
---|---|
Global | Documento |
Local | Função |
Bloco | Condicional ou Loop |
Recebe um valor constante, ou seja, uma vez declarada não pode ter um novo valor atribuído a ela. Lembrando que não precisa receber necessariamente um valor numérico ou string - podemos atrelar a essa const uma função também. Exemplo:
const soma = (x, y) => { return x + y }
A const, se declarada globalmente, pode ser acessada globalmente. Mas, se for declarada dentro de um bloco ou em escopo local, se limita a esse escopo.
A let funciona de maneira muito semelhante à const, ou seja: se declarada dentro de um bloco ou escopo local, respeita tal escopo. No entanto pode ter um novo valor atribuído a ela n vezes.
Atualmente, no ES6 (EcmaScript6) não costuma ser utilizada - isso pois, diferente da let, ela transcende o escopo de bloco (não o respeita). A var pode ter seu valor atribuído n vezes também.
Exemplo do problema de utilizarmos a var:
console.log('\nUSANDO VAR\n');
for (var i = 0; i < 5; i++){
console.log(`var i dentro do loop for: ${i}`)
}
console.log(`var i fora do loop for: ${i}`)
/*
* USANDO VAR
* var i dentro do loop for: 0
* var i dentro do loop for: 1
* var i dentro do loop for: 2
* var i dentro do loop for: 3
* var i dentro do loop for: 4
* var i fora do loop for: 5
*/
console.log('\nUSANDO LET\n');
for (let j = 0; j < 5; j++){
console.log(`let j dentro do loop for: ${j}`)
}
console.log(`let j fora do loop for: ${j}`)
/*
* USANDO LET
* var j dentro do loop for: 0
* var j dentro do loop for: 1
* var j dentro do loop for: 2
* var j dentro do loop for: 3
* var j dentro do loop for: 4
* var j fora do loop for: **j is not defined**
*/
Se declararmos uma variável sem definir se é var, let ou const, essa variável terá escopo global, independente do escopo em que foi declarada. Exemplo:
var x = 1 // Global
y = 2 // Global
function()
{
var x = 1 // Local
y = 2 // Global
// Executando uma função anônima (sem nome)
(function()
{
var n = 1; // Local
x = 2; // Herda do escopo 'superior'
z = 3; // Global
}())
}
No exemplo abaixo temos duas closures - soma5() e soma10(). Ambas compartilham o mesmo corpo de função, mas possuem ambientes distintos (cada uma tem um valor distinto para x). Ademais, usando a função criarSomador(), tornamos a função anônima de retorno dessa função (function (y) { return x + y }) é privada.
function criarSomador(x) {
return function(y) {
return x + y;
};
}
var soma5 = criarSomador(5);
var soma10 = criarSomador(10);
console.log(soma5(2)); // 7
console.log(soma10(2)); // 12
Conheça 3 princípios muito úteis na hora de criarmos nossos scripts:
DRY consiste em ter consistência no seu código e manter uma funcionalidade específica única, sem repetí-la diversas vezes para contemplar diferentes cenários (a solução deve ser consistente e prever essas variações dentro da própria solução, de modo que sempre que nos depararmos com essa questão específica, possamos recorrer a essa funcionalidade).
Ao mesmo tempo que temos o DRY, não podemos tentar prever toda e cada situação em cada funcionalidade implantada. Devemos deixar nosso código o mais enxuto possível - claro que devemos prever alguns cenários, ter certa flexibilidade e planejarmos nossos scripts - mas não é uma boa prática tentarmos prever cenários que muito provavelmente jamais existirão.
Por fim, devemos sempre optar pela simplicidade - ela confere maior legibilidade ao código (seja para outros devs que colaborem no mesmo projeto ou para nós mesmos, ao revisitarmos um projeto antigo), maior performance (normalmente) e maior fluidez no processo de desenvolvimento. Dica: após 'finalizar' seu código, dê mais uma revisada e tente entender se é possível simplificar sua(s) solução(ões) - verá que muitas vezes aquilo que parecia ser a solução acaba se tornando apenas um rascunho da melhor solução.
É isso galera! Qualquer dúvida, nos procurem no Slack da turma! Boa aula!