Linguagem de programação que roda no Navegador do usuário (front-end).
Ao clicar em algum botão da página e aparecer uma janela, isso é o JavaScript. A alteração do site ou do aplicativo é feita conforme a interação do usuário.
Roda também no computador (back-end).
Podemos criar aplicações web, mobile (React Native), desktop (Electron).
Dentre as empresas famosas que utilizam JavaScript, estão: Facebook (Instagram, WhatsApp), Google (Youtube, Gmail, Drive), Uber, Netflix e Tiktok.
99.99% dos sites na web usam JavaScript. É uma linguagem obrigatória para quem programa o front-end web.
A comunidade cresce cada vez mais e a linguagem está sempre evoluindo.
Essa palavra significa escrever textos de uma maneira correta. Toda linguagem tem sintaxe, seja idioma ou uma linguagem de programação.
Uma boa comunicação necessita de uma boa sintaxe. Cerca de 82% dos erros para iniciantes estão na sintaxe, ou seja, na maneira de não escrever corretamente.
Por exemplo, se eu escrever "consolelog" e não "console.log", há um erro de sintaxe que vai fazer o computador entender que é uma função e não um objeto (console) para depois uma função (log).
O exemplo certo:
console.log("Bem-vindos ao Discovery!");
Precisamos ficar atentos à importância da sintaxe.
-
Browser - o JavaScript já está pronto no navegador, já está rodando. Ao clicar em F12 no Chrome, por exemplo, é possível, na aba Console, escrever em JavaScript.
-
Codepen.io - Ambiente onde você pode escrever tanto código em JavaScript, quanto HTML e CSS.
-
Editor de código - Em Visual Studio Code, por exemplo. Tanto por tags "script" no HTML ou em arquivos com extensão ".js" (inserindo-o no HTML).
Criamos um arquivo, como "script", com extensão .js e com o código em JavaScript. Inserimos esse arquivo JS dentro do HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Adicionando arquivo JS</title>
</head>
<body>
<script src="./script.js"></script>
</body>
</html>
Para ignorar código em JavaScript ou comentá-lo, podemos usar "//" em caso de uma linha e "/* */" para mais de uma linha/comentário de bloco.
Para aprender uma linguagem nova, precisamos entender sua gramática, ou seja, os elementos da linguagem e suas combinações; escrever corretamente.
No caso do JavaScript, precisamos entender os tipos de dados e quais as combinações para, então, desenvolvermos nossa habilidade de programar corretamente com JavaScript.
O vocabulário é o conjunto de termos e expressões da linguagem ou o agrupamento de palavras. Quanto mais você vai aprendendo os termos e expressões da linguagem, maior será sua fluência e maior será o seu vocabulário.
Então, precisamos:
- Saber como escrever;
- Saber os significados;
- Continuar aprendendo para crescer nosso vocabulário.
O foco deste estudo agora é conceito e escrita. Ao entender o conceito, olhar e entender como escreve, vamos depois para a prática.
Vamos aprender os tipos de dados mais utilizados na linguagem. É possível aprender 80% de uma língua nova com cerca de 20% do vocabulário.
É uma cadeia de caracteres, por exemplo, "abc", "a" é seguindo de "b" que é seguido de "c". E, obviamente, essa cadeia forma textos.
Para utilizar strings, devo inserir o conteúdo dentro de uma dessas opções:
- "": aspas duplas;
- '': aspas simples;
- ``: template literals ou template strings.
Quando usar cada uma delas?
Por exemplo:
console.log("Angélica")
Se dentro desse texto eu vou colocar algo que levará aspas simples, então em volta será aspas duplas e vice-versa:
console.log("Angélica é 'web developer'")
console.log('Angélica é "web developer"')
Posso utilizar as template strings (crases) com aspas duplas e aspas simples; elas permitem que eu faça multilinhas, além de inserir expressões de linguagem/valores de interpolação.
console.log(`"Quanto é 1 + 1?" 'R': ${1 + 1}`)
Numbers são tipos de dados identificadores de números.
Eles podem ser positivos ou negativos:
- Inteiros (ex: 33);
- Reais (ex: 12.5);
- NaN (not a number);
- Infinity (infinito).
Entendendo o NaN
Se eu divido, por exemplo, 12.5 por 12, o resultado é 1.041. Agora, se eu divido por uma string, por exemplo, 12.5 / "dividido por 2", ele não entende e por isso retorna "não é um número".
Mas se tentarmos 12.5 / "2", ele converte a string para número e consegue retornar o resultado.
Entendendo o Infinity
Posso igualar o valor 12.5 a Infinity, que vai retornar se é verdadeiro ou falso, se é igual a infinito.
console.log(12.5 === Infinity)
Nesse caso, o retorno é falso.
Importante notar que Infinity tem que ser escrito com "i" maiúsculo, se não dá erro de referência.
Bolean é um tipo de dado que só tem dois valores: verdadeiro (true) ou falso (false).
É muito usual porque sempre vamos encontrar verdadeiro/falso em lógica da programação. Por exemplo: vou fazer um café. Minha xícara está suja? Se for verdadeiro, vou limpar; se for falso, vou colocar o café na xícara.
São dois tipos de dados que podem ser confundidos.
Undefined:
- É uma palavra-chave para dizer que algo é indefinido.
Null:
- É uma palava-chave para dizer que algo é nulo, ou seja, é diferente de indefinido por ser considerado como um objeto que não possui nada dentro.
É um tipo de dado estrutural, que cria/causa uma estrutura.
Todo objeto, seja no mundo real ou abstrato, possui proriedades (atributos) e funcionalidades (métodos).
Para escrever um objeto em JavaScript, abrimos chaves, colocamos o nome da propriedade e, em seguida, seu valor:
{
propriedade: "valor",
}
Exemplo onde o objeto tem propriedades e funcionalidade "andar":
{
nome: "Angélica",
idade: 30,
andar: function(){
console.log("andar")
}
}
Os vetores (arrays) são tipos de dados estruturais e servem para criação de listas e agrupamento de dados.
Exemplo com dados de uma lista (de um array) de itens de supermercado:
console.log(["Melancia", "Batata", "Melão", "Kiwi", "Uva"]);
Conforme o ECMAScript standard, órgão que padroniza o JavaScript, existem 9 tipos de dados:
-
Data types:
- primitive / primitive value
- structural
- structural primitive
-
Primitivos:
- String
- Number
- Boolean
- undefined
- Symbol
- BigInt
Os primitivos não são objetos e seus valores são imutáveis.
Ou seja, uma string "abc" é e só pode ser "abc", o que é diferente de "ABC", "cba" e assim por diante.
- Estruturais:
- Object
- Array
- Map
- Set
- Date
- ...
- Function
Como o objeto é um tipo estrutural, ele tem uma estrutura e, assim, recebe propriedades (atributos) e funcionalidades (métodos). Todo objeto tem algum tipo de atributo e funcionalidade.
- Primitivo Estrutural (Structural Root Primitive):
- null
O null é o único que é primitivo e ao mesmo tempo estrutural porque está na categoria de objeto, apesar de ser um objeto vazio. Se tem algo dentro do objeto, ele deixa de ser null e se torna um objeto.
Toda estrutura do JavaScript faz mais sentido quando entendemos BEM o objeto, pois praticamente todos os valores que tivermos estarão "embrulhados" em um objeto para que possamos ter mais poder ao acessá-los.
Se você quer guardar coisas em seu armário, ele precisa ter espaço. Se não, é preciso retirar algumas coisas para que você possa ter espaço para as novas que quer colocar.
Na programação, as variáveis funcionam mais ou menos desse jeito. O armário é a memória do computador, que tem que ter espaço (variável) para receber os tipos de dados (as coisas que você gostaria de guardar no armário).
As variáveis:
- São nomes simbólicos para receber algum valor;
- São atalhos de código;
- Podem ser chamadas de identificadores.
Palavras reservadas
- var
- let
- const
São as três palavras reservadas para criar uma variável e cada uma tem um comportamento diferente.
Enquanto no var e no let você consegue reatribuir valores, na const isso não é possível.
O JavaScript é uma linguagem fracamente tipada e dinâmica; as variáveis não precisam ter um tipo previamente definido e podemos mudar o seu conteúdo.
Em algumas linguagens de programação nós precisamos definir o tipo para criar a variável e depois atribuir um valor para a mesma. Vejamos a diferença:
//JavaScript
let clima = "Quente";
//TypeScript
let clima: String = "Quente";
Acima significa que "clima" é e sempre será uma string, ou seja, é uma variável tipada e estática.
Para eu verificar o tipo de uma variável definida em JavaScript, posso utilizar "typeof":
//JavaScript
let clima = "Quente";
console.log(typeof clima); // string
Escopo determina a visibilidade de alguma variável no JavaScript.
Um block statement significa declaração de bloco:
//vamos iniciar um bloco
{
//aqui dentro é um bloco e posso colocar qualquer código.
} //aqui fechamos o bloco
O bloco também criará um novo escopo que é chamado de "block-scoped".
var
O var é global, local e pode funcionar fora de um escopo de bloco.
Global é tudo que está executando na aplicação.
Local é o que somente existe dentro de um escopo.
Dentro das chaves temos o escopo local. Já tudo que está no bloco de código é o escopo global.
//erro de referência porque x não foi declarado
console.log("> existe x antes do bloco? ", x);
{
}
//x existe antes do bloco
var x = 0;
console.log("> existe x antes do bloco? ", x);
{
}
// retorno é undefined: x existe, mas não tem valor definido para ele
console.log("> existe x antes do bloco? ", x);
{
var x = 0;
}
Por que acontece "undefined" acima?
Porque o código JavaScript vem lendo de cima para baixo, linha a linha. Apesar de x só começar a ser declarado depois do console.log, o JavaScript tem um conceito muito interessante em relação à var que é levá-la para o topo do código, como se fosse isso:
// retorno é undefined
var x
console.log("> existe x antes do bloco? ", x);
{
= 0;
}
Como var é global, o JavaScript "puxa" de dentro do bloco e declara lá em cima, sem o desenvolvedor conseguir ver, em um conceito chamado de hoisting (elevação), para depois atualizar o valor.
Const e let são locais e só funcionam no escopo onde foram criadas. No var, como visto acima, acontece o hoisting.
let
Nos dois casos a seguir, acontecerá erros de referência/de visibilidade de variável, que está somente no escopo onde foi criada:
console.log("> existe y antes do bloco? ", y);
//não existe no escopo global, só no escopo local
{
let y = 0;
}
{
let y = 0;
}
console.log("> existe y depois do bloco? ", y);
//não existe no escopo global, só no escopo local
No caso abaixo, ele sabe que existe w, mas não pode acessá-lo antes da inicialização, já que o let não permite o hoisting.
{
console.log("> existe w antes? ", w);
let w = 0;
//sabe que existe, mas não pode acessar 'w'
}
Só vai funcionar depois que eu declarei e adicionei um valor a ele:
{
let w = 0;
console.log("> existe w? ", w);
//existe, valor 0
}
No caso abaixo, começo declarando z globalmente como 1. Mas como trago essa variável "de fora", quando ela entra no escopo local ela atualiza, resultando em saída 0 em ambas as perguntas.
Como dentro do escopo não há uma palavra chave reservada, ou seja, "let z = 0", ele sobe um escopo para procurar o z em algum lugar. Assim, ele entende que só precisa atualizar o valor para 0.
let z = 1;
{
z = 0;
console.log("existe z?", z); //0
}
console.log("existe z depois do bloco? ", z); //0
const
A const, como o nome já indica, é constante. Ou seja, não muda. Você não pode atualizar valor quando uma variável for constante.
const c = 1;
{
c = 0;
console.log("existe c?", c);
}
console.log("existe c depois do bloco? ", c);
Mas é possível criar uma nova constante em um outro escopo, mesmo que o nome seja igual, pois pertencem a "grupos" diferentes:
const c = 1;
{
const c = 0;
console.log("existe c?", c); //0
}
console.log("existe c depois do bloco? ", c); //1
Usualmente, desenvolvedores preferem utilizar let e const porque var acaba sendo muito flexível e permite alterações em diferentes escopos, podendo causar bugs.
Para criar nomes
- JS é case-sensitive (sensível ao caso);
- JS aceita a cadeira de caracteres Unicode (você pode colocar aspas e acentuções nos nomes de suas variáveis).
Eu posso:
- Iniciar com esses caracteres especiais: "$" "_" ;
- Iniciar com letras;
- Colocar acentos;
- Letras maiúsculas e minúsculas fazem diferença.
Não posso:
- Iniciar com números;
- Colocar espaços vazios no nome.
Ideal é:
- Criar nomes que façam sentido;
- Que expliquem o que a variável é ou faz;
- Utilizar camelCase;
- Ou utilizar snake_case;
- Escrever em inglês.
Declaração de variável (declaration):
var name;
Atribuição de valores (assignment):
var name = "angelica";
Verficação de tipo de dado da variável:
console.log(typeof name);
let age = 20; //number
let isHuman = true; //boolean
Eu posso fazer declarações conjuntas, ou seja, agrupá-las deste modo:
let age, isHuman;
age = 18;
isHuman = true;
console.log(age, isHuman);
//múltiplos argumentos na função
Continuando com os exemplos anteriores, se eu quisesse imprimir em textos no console, poderia fazer:
console.log("A Angélica tem 18 anos.");
Mas podemos melhorar essa declaração através de substituição dos valores por variáveis, concatenando-as:
console.log("A " + name + " tem " + age + " anos.");
//escrita de texto mais variáveis concatenando valores
Mas pode ficar melhor interpolando valores e utilizando template string:
console.log(`A ${name} tem ${age} anos.`);
//interpolando valores com template strings
Fazendo a criação de um objeto com propriedade e valor:
const person = {
name: "Jorge",
age: 30,
weight: 88.6,
isAdmin: true,
};
Para imprimir dados específicos via console.log, faço:
console.log(person.name);
console.log(`${person.name} pesa ${person.weight} quilos.`);
Criando um array de strings:
const animals = ["Lion", "Monkey", "Cat"];
Ao fazer console.log(animals), o array é exibido da seguinte forma:
(3) ["Lion", "Monkey", "Cat"]
0: "Lion"
1: "Monkey"
2: "Cat"
length: 3
Para acessar valores específicos dentro de um array, eu tenho que pegar a posição do array e insiro dentro de colchetes ("0", "1" e "2" são número e por isso causaria uma confusão se eu tentasse acessá-los diretamente, por não serem interpretados como posições):
console.log(animals[0]); //"Lion"
Para eu saber o total de elementos do array, utilizo o length e acesso deste modo:
console.log(animals.length); //3
Lembrando que array pode ser composto por outros tipos, apesar de não ser recomendado.
No caso abaixo, "Lion" e "Monkey" são strings, enquanto "Cat" faz parte de um objeto:
const animals = [
"Lion",
"Monkey",
{
name: "Cat",
age: 3,
},
];
Ao acessar console.log(animals[2]), é mostrado:
{name: "Cat", age: 3}
age: 3
name: "Cat"
E posso acessar uma propriedade desse objeto:
console.log(animals[2].name); //"Cat"
Vamos supor que eu queria criar um aplicativo de frases motivacionais:
console.log("Estudar é muito bom.");
console.log("Paciência é persistência.");
console.log("Revisão é a mãe do aprendizado.");
console.log("Sempre haverá um próximo nível.");
Por algum motivo, eu quero repetir essas frases mais vezes. Existe um jeito para fazer essa repetição sem necessariamente precisar do "control C + control V". Esse jeito é com função.
A função tem esses motivos:
- Explicar o que o código quer dizer;
- Agrupar o código;
- Repetir o código.
Declaração da função (function statement/declaration)
Função também é um tipo de dado estrutural, bem organizada, para receber o código/agrupar um código que pode ser utilizado/reutilizado mais tarde.
Sua criação é feita da seguinte maneira:
function createPhrases() {
console.log("Estudar é muito bom.");
console.log("Paciência é persistência.");
console.log("Revisão é a mãe do aprendizado.");
console.log("Sempre haverá um próximo nível.");
}
Todo esse bloco fica guardado em memória pelo JavaScript para que ele seja chamado a qualquer momento.
É como se o JavaScript percorresse o caminho todo do código e guardasse o nome da função como uma espécie de "atalho"; então, quando a função fosse chamada, ele lembraria desse atalho e o usaria.
Chamar a função (execute, run, call, invoke function)
Simplesmente a chamo pelo nome juntamente aos seus parênteses:
createPhrases();
Podemos declarar funções dentro de variáveis.
const sum = function () {};
A função acima é chamada de função anônima (ou function expression), pois não possui um nome. Entretanto, ela segue a mesma ideia da declaração de função aprendida anteriormente para ser invocada:
sum();
Para eu calcular uma soma nesta função, eu tenho que passar argumentos para ela dentro dos parênteses, para que ela os receba quando for invocada.
Ela recebe esses argumentos (que no caso abaixo são 2 e 3) através de parâmetros (parameters), escritos como number1 e number2:
const sum = function (number1, number2) {};
sum(2, 3); //argumentos da função executada
É como se, "por baixo dos panos", ele estivesse fazendo essa atribuição nesse escopo:
const sum = function (number1, number2) {
let number1 = 2;
let number2 = 3;
};
sum(2, 3);
Somando os números
O trecho abaixo retorna 5, funciona com sucesso:
const sum = function (number1, number2) {
console.log(number1 + number2);
};
sum(2, 3);
Agora, caso eu acesse algum desses parâmetros fora do escopo da função, eu tenho erro de referência:
const sum = function (number1, number2) {
console.log(number1 + number2);
};
console.log(number1);
sum(2, 3);
//Uncaught ReferenceError: number1 is not defined
Agora que conheço que é possível repetir o código de soma e fazer diferentes somas passando argumentos, vamos para algo novo.
const sum = function (number1, number2) {
console.log(number1 + number2);
};
let number1 = 34;
let number2 = 25;
sum(number1, number2);
Esse number1 e esse number2 que estão fora não fazem parte do escopo do bloco de código dentro das chaves, como já vimos. Ao fazer isso, teremos o cálculo "59".
E agora? qual seria o resultado impresso pelo console nessa ocasião?
const sum = function (number1, number2) {
console.log(number1 + number2);
};
let number1 = 34;
let number2 = 25;
console.log(`o número 1 é ${number1}`);
console.log(`o número 2 é ${number2}`);
console.log(`a soma é ${sum(number1, number2)}`);
No caso, a impressão será:
O número 1 é 34
O número 2 é 25
59
a soma é undefined
Como na linha console.log(
a soma é ${sum(number1 + number2)});
o código ainda está construindo a frase e interpolando o valor; ele entra nessa expressão "sum(number1 + number2)
", executa a função function (number1, number2)
e nessa execução contém "console.log(number1 + number2)
", fazendo com que o código imediatamente imprima 59
.
Ao término dessa execução, ele vai retornar toda a função (já que toda função tem que retornar algo). Só que quando uma função não possui a palavra-chave return, como no caso acima, ela vai retornar undefined.
Então, para acertar o código, fica:
const sum = function (number1, number2) {
total = number1 + number2;
return total;
};
let number1 = 34;
let number2 = 25;
console.log(`o número 1 é ${number1}`);
console.log(`o número 2 é ${number2}`);
console.log(`a soma é ${sum(number1, number2)}`);
Agora temos outra coisa acontecendo. Dentro dessa função não temos nenhum console.log
, então quando o código começar a execução da função sum(number1, number2)
, vai em function (number1, number2)
fazer o registro de cada número em cada parâmetro e colocar em total
, depois vai achar a linha return
e inserir esse total.
Como uma função sempre que encontra uma linha return
para a execução retorna o que vem logo à frente, nesse caso será retornado a variável total
, que é justamente o resultado da função que chamo em console.log(`a soma é ${sum(number1, number2)}`)
.
Vale alertar que total foi declarado sem var/let/const e pode ser acessada fora do escopo de função após console.log(`a soma é ${sum(number1, number2)}`)
(se tentar acessar antes, vai dar erro de referência).
Curioso que se fosse um escopo de bloco de código e não de função, se eu declarasse var = total eu conseguiria acessar fora desse escopo; mas, tratando-se de escopo de função, nem mesmo o var consegue ser visto de fora.
Imagine que a função é um liquidificador, que vai receber fruta1 e fruta2 e vai mesclá-las.
function FazerSuco(fruta1, fruta2) {
return = fruta1 + fruta2
}
FazerSuco('banana', 'maçã')
FazerSuco('banana', 'maçã')
significa que ao passar essas frutas, vai entrar na função e fazer o suco.
Mas é preciso de um copo para receber esse suco; esse copo vai ser uma constante e depois vou mostrar no console.log o que tem nesse copo:
function FazerSuco(fruta1, fruta2) {
return = fruta1 + fruta2
}
const copo = FazerSuco('banana', 'maçã')
console.log(copo)
Suponhamos que agora vamos criar uma função que cria um pensamento e recebe um assunto.
A função vai receber o mesmo nome da variável que tem do lado de fora e só vai retornar o valor:
let subject;
function createThink(subject) {
return subject;
}
console.log(createThink(subject));
Quando eu declaro uma variável e não passo valor, seu retorno, como já visto, é undefined.
Abaixo, então, insiro a string "create video" como valor da variável subject:
let subject = "create video";
function createThink(subject) {
return subject;
}
console.log(subject);
console.log(createThink(subject));
//retorno em ambos os casos de console.log será create video
Abaixo, já faço diferente: digo, dentro da função, que subject vai receber "study". Assim, não me importa o que eu estou passando em function createThink(subject)
, pois o subject será atualizado para "study".
let subject = "create video";
function createThink(subject) {
subject = "study";
return subject;
}
console.log(subject); //create video
console.log(createThink(subject)); //study
Caso a função createThink
não tivesse parâmetro, o retorno de ambos console.log
seria "study", pois ao executar a função ele pegará o subject do escopo anterior para sobrescrever.
let subject = "create video";
function createThink() {
subject = "study";
return subject;
}
console.log(subject); //study
console.log(createThink(subject)); //study
Então, só existe no escopo {subject = "study"; return subject;}
um novo subject se por acaso eu colocá-lo como parâmetro em function createThink(subject)
.
Ou seja: esse subject nesse escopo tem o significado de "study":
function createThink(subject) {
subject = "study";
return subject;
}
E esse aqui tem outro significado:
let subject = "create video";
Mas a partir do momento que eu não crio um parâmetro, então esse subject de dentro do escopo da função vai agora buscar o do escopo anterior e atualizá-lo no retorno.
let subject = "create video";
function createThink() {
subject = "study";
return subject;
}
console.log(createThink(subject)); //study
console.log(subject); //study
E se eu quiser imprimir console.log(createThink())
, o retorno será undefined, enquanto console.log(subject)
será "study".
Outro teste de impressão: criando a variável subject sem passar algum valor e imprimindo um console.log(subject)
antes de chamar createThink()
, mesmo sem passar nenhum pensamento como argumento:
let subject;
function createThink() {
subject = "study";
return subject;
}
console.log(subject); //undefined
console.log(createThink()); //study: retorno da função
console.log(subject); //study: subject declarado sem valor e que foi atualizado fora
Agora, se a função não retornar nada, ao fazer console.log(createThink())
vai aparecer undefined, pois toda função que não tem retorno resulta em undefined.
let subject;
function createThink() {
subject = "study";
}
console.log(subject); //undefined
console.log(createThink()); //undefined
console.log(subject); //study: subject declarado sem valor e que foi atualizado fora
Aqui ocorreria só a atualização, não retornaria nada:
let subject;
function createThink() {
subject = "study";
}
console.log(subject); //undefined
createThink(); //não é mostrado no console, ela só atualiza a variável para study
console.log(subject); //study: subject declarado sem valor e que foi atualizado fora