JavaScript é uma linguagem de programação de alto nível, interpretada e orientada a objetos. É uma das principais tecnologias da web, juntamente com HTML e CSS. É amplamente utilizada para criar páginas web interativas, aplicativos web e servidores. JavaScript permite a manipulação dinâmica de conteúdo, controle de multimídia, animação de gráficos e muito mais.
avaScript foi criado por Brendan Eich em 1995 enquanto trabalhava na Netscape. Desenvolvida em apenas dez dias, a linguagem foi inicialmente chamada de Mocha, depois LiveScript, e finalmente JavaScript. Seu objetivo era adicionar interatividade a páginas web. Em 1996, a Microsoft lançou o JScript, e para garantir a compatibilidade entre navegadores, JavaScript foi padronizado pela Ecma International como ECMAScript em 1997.
Com o tempo, JavaScript evoluiu, tornando-se essencial para o desenvolvimento web, com o surgimento de bibliotecas e frameworks como jQuery, Angular, React, e Vue.js, além da plataforma Node.js, que permite a execução no lado do servidor. Hoje, JavaScript é amplamente utilizado e continua a evoluir, permanecendo crucial para experiências web dinâmicas.
JavaScript, inventado por Brendan Eich, alcançou o status de um padrão ECMA em 1997 e adotou o nome oficial ECMAScript. Esta linguagem evoluiu através de várias versões, nomeadamente ES1, ES2, ES3, ES5, e a transformadora ES6. Estas atualizações desempenharam um papel crucial na melhoria e padronização do JavaScript, tornando-o amplamente utilizado e valioso no campo em constante mudança do desenvolvimento web.
Os navegadores modernos possuem consoles de desenvolvedor onde você pode executar código JavaScript diretamente.
- Abra seu navegador (por exemplo, Chrome, Firefox, Edge).
- Pressione
F12
ouCtrl+Shift+I
(Windows/Linux) ouCmd+Opt+I
(Mac) para abrir as ferramentas de desenvolvedor.- Vá para a aba "Console".
- Digite seu código JavaScript e pressione
Enter
.Exemplo: console.log("Hello, World!");Você pode inserir JavaScript diretamente em um arquivo HTML.
Passos:
- Crie um arquivo HTML com uma estrutura básica.
- Adicione a tag
<script>
onde deseja incluir seu código JavaScript.Exemplo: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript Example</title> </head> <body> <h1>My First JavaScript</h1> <script> console.log("Hello, World!"); </script> </body> </html>Abra este arquivo HTML no navegador e o JavaScript será executado automaticamente.
Se você deseja executar JavaScript fora de um navegador, você pode usar Node.js.
Passos:
- Instale o Node.js.
- Crie um arquivo JavaScript (por exemplo,
script.js
).- Adicione seu código JavaScript ao arquivo.
Exemplo (`script.js`): console.log("Hello, World!");
- Abra o terminal ou prompt de comando.
- Navegue até o diretório onde seu arquivo JavaScript está localizado.
- Execute o arquivo usando o comando
node
.node script.js
Declaração:
Você pode declarar uma variável usandovar
da seguinte maneira:// Sintaxe: var nomeDaVariavel = Valor; // Exemplo: var nome = "Rick Lustri";Reatribuição:
Variáveis declaradas comvar
podem ser reatribuídas.var nome = "Rick Lustri"; // declaração da variavel com o valor console.log(nome); // Rick Lustri nome = "Henrique Lustri"; // reatribuindo um valor para a variavel console.log(nome); // Henrique LustriRe-declaração:
É possível re-declarar a mesma variável usandovar
sem causar erros.var nome = "Rick Lustri"; // declaração da variavel com o valor var nome = "Henrique Lustri"; // declaração da variavel com o valor console.log(nome); // Henrique LustriAs variáveis declaradas com
var
têm escopo de função ou escopo global:
- Escopo global: Se uma variável for declarada fora de qualquer função, ela será visível em qualquer lugar no código.
- Escopo de função: Se uma variável for declarada dentro de uma função, ela será visível apenas dentro dessa função.
var nome = "Rick Lustri"; // nome está visível em qualquer lugar no código function example() { console.log(nome); // Rick Lustri } console.log(nome); // Rick Lustrifunction example() { var nome = "Rick Lustri"; // nome está visível apenas dentro desta função console.log(nome); // Rick Lustri } console.log(nome); // Erro: nome não está definidoHoisting de
var
com declaração e inicialização
- Inicialização com
undefined
: A variávelvar
é inicializada automaticamente comundefined
durante o hoisting, mesmo que seu valor seja atribuído posteriormente no código.console.log(nome); // undefined var nome = "Henrique"; console.log(nome); // HenriqueNeste exemplo, o JavaScript interpreta o código da seguinte maneira durante a execução:
var nome; // declaração é elevada para o topo do escopo console.log(nome); // undefined nome = "Henrique"; // inicialização ocorre no local original console.log(nome); // HenriqueHoisting de
var
com declaração no escopo de função
- Escopo de função: As variáveis
var
têm escopo de função ou escopo global, o que pode levar a comportamentos inesperados se não forem compreendidos corretamente.function example() { console.log(nome); // undefined var nome = "Rick"; console.log(nome); // Rick } example();Dentro da função
example
, o código é interpretado da seguinte maneira:function example() { var nome; // declaração é elevada para o topo da função console.log(nome); // undefined nome = "Rick"; // inicialização ocorre no local original console.log(y); // Rick } example();
Declaração:
Você pode declarar uma variável usandolet
da seguinte maneira:// Sintaxe: let nomeDaVariavel = Valor; // Exemplo: let idade = 22;Reatribuição:
Variáveis declaradas comlet
podem ser reatribuídas da mesma forma quevar
.let idade = 22; // declaração da variavel com o valor console.log(idade); // 22 idade = 23; // reatribuindo um valor para a variavel console.log(idade); // 23Re-declaração:
Ao contrário devar
, tentar re-declarar a mesma variável comlet
no mesmo escopo gera um erro.let idade = 22; // declaração da variavel com o valor let idade = 23; // Erro: variavel 'idade' ja foi declarada console.log(idade); // 22As variáveis declaradas com
let
têm escopo de bloco:
- Escopo de bloco: Uma variável declarada com
let
é visível apenas dentro do bloco onde foi declarada, seja um bloco de função, um blocoif
, um loopfor
, etc.function example() { let idade = 22; // 'idade' está visível apenas dentro deste bloco `function` console.log(x); // 22 } console.log(idade)); // Erro: 'idade' não está definidaHoisting de
let
com declaração
- Erro
ReferenceError
: Ao contrário devar
, uma variávellet
não é inicializada comundefined
durante o hoisting. Tentar acessá-la antes de sua declaração resulta em um erroReferenceError
.console.log(idade); // ReferenceError: Não é possível acessar 'idade' antes da inicialização let idade = 22; console.log(idade); // 22Com
let
, a variável não é inicializada comundefined
durante o hoisting. Em vez disso, ocorre um erroReferenceError
se tentarmos acessar a variável antes de sua declaração.
Durante a execução, o código é interpretado assim:let idade; // declaração é elevada para o topo do escopo console.log(idade); // ReferenceError: Não é possível acessar 'idade' antes da inicialização idade = 22; // inicialização ocorre no local original console.log(idade); // 22Hoisting de
let
com declaração no escopo de função
- Escopo de bloco:
let
tem escopo de bloco, o que significa que é limitada ao bloco em que é declarada. Isso é útil para evitar problemas de vazamento de variáveis e colisões de nomes.function example() { console.log(idade); // ReferenceError: Não é possível acessar 'idade' antes da inicialização let idade = 22; console.log(y); // 22 } example();Dentro da função
example
, o código é interpretado da seguinte maneira:function example() { let idade; // declaração é elevada para o topo da função console.log(idade); // ReferenceError: Não é possível acessar 'idade' antes da inicialização idade = 22; // inicialização ocorre no local original console.log(idade); // 22 } example();
Declaração:
Você pode declarar uma variável constante usandoconst
da seguinte maneira:// Sintaxe: const nomeDaVariavel = Valor; // Exemplo: const corFavorita = "Vermelho";Reatribuição:
Ao contrário devar
elet
, variáveis declaradas comconst
não podem ser reatribuídas. Isso significa que seu valor inicial não pode ser alterado após a atribuição inicial.const corFavorita = "Vermelho"; corFavorita = "Azul"; // Erro: Atribuição a variável constante. console.log(corFavorita); // VermelhoRe-declaração:
Assim comolet
, tentar re-declarar a mesma variável comconst
no mesmo escopo gera um erro.const corFavorita = "Vermelho"; const corFavorita = "Roxo"; // Erro: variavel 'corFavorita' ja foi declarada console.log(corFavorita); // VermelhoAs variáveis declaradas com
const
têm escopo de bloco:
- Escopo de bloco: Uma variável declarada com
const
é visível apenas dentro do bloco onde foi declarada, seja um bloco de função, um blocoif
, um loopfor
, etc.function example() { const corFavorita = "Vermelho"; // corFavorita está visível apenas dentro deste bloco `function` console.log(corFavorita); // Vermelho } console.log(corFavorita); // Erro: corFavorita não está definidaHoisting de
const
com declaração
- Erro
ReferenceError
: Semelhante alet
, uma variávelconst
não é inicializada durante o hoisting. Tentar acessá-la antes de sua declaração resulta em um erroReferenceError
.- Atribuição após inicialização: Uma vez inicializada, uma variável
const
não pode ser reatribuída. Isso significa que seu valor não pode mudar depois de ser definido.console.log(corFavorita); // ReferenceError: Não é possível acessar 'corFavorita' antes da inicialização const corFavorita = "Vermelho"; console.log(corFavorita); // VermelhoCom
const
, assim comolet
, a variável não é inicializada durante o hoisting. Tentar acessá-la antes de sua declaração resulta em um erroReferenceError
.
Durante a execução, o código é interpretado assim:const corFavorita; // declaração é elevada para o topo do escopo console.log(corFavorita); // ReferenceError: Não é possível acessar 'corFavorita' antes da inicialização corFavorita = "Vermelho"; // inicialização ocorre no local original console.log(corFavorita); // VermelhoHoisting de
const
com declaração no escopo de função
- Escopo de bloco:
const
também possui escopo de bloco, o que é útil para limitar a visibilidade da variável ao bloco em que é declarada.function example() { console.log(corFavorita); // ReferenceError: Não é possível acessar 'corFavorita' antes da inicialização const corFavorita = "Vermelho"; console.log(corFavorita); // Vermelho } example();Dentro da função
example
, o código é interpretado da seguinte maneira:function example() { const corFavorita; // declaração é elevada para o topo da função console.log(corFavorita); // ReferenceError: Não é possível acessar 'corFavorita' antes da inicialização corFavorita = "Vermelho"; // inicialização ocorre no local original console.log(corFavorita); // Vermelho } example();
Ao programar em JavaScript, é importante seguir regras específicas para nomear variáveis, garantindo clareza e evitando
Permitido:
Nomes de variáveis podem conter apenas letras, letras e números,_
(sublinhado) e$
(cifrão).// Letras: var abc; // É permitido // Variáveis podem consistir apenas de letras. // Letras e números: var numero1 // É permitido // Variáveis podem conter letras e números // Sublinhado: var com_sublinhado; // É permitido var _sublinhado_; // É permitido (sublinhado no início e no final) // Pode ser usado em qualquer posição no nome da variável. // Cifrão: var $cifrao; // É permitido vat cifrao$; // É permitido (cifrão no início e no final, embora seja mais comum no final) // Pode ser usado em qualquer posição no nome da variável.Não permite:
Nomes de variáveis não podem começar com números, ter espaços dentro dos nomes ou usar palavras reservadas.// Letras e números: var 1numero; // Não é permitido // Espaços: var meu nome; // Não é permitido // Palavras reservadas: var var; // Não é permitido var let; // Não é permitido var function; // Não é permitidoSeja Descritivo: Escolha nomes que claramente descrevam o que a variável representa.
// É aceito, mas é ilegível caso outra pessoa pegue seu código para ler: var x; var y; var z; // O correto de se usar: var nomeDeUsuario; var idadeDoUsuario; var diaDoMes;
- Os exemplos mostram a diferença entre variáveis com nomes genéricos e variáveis com nomes descritivos.
- Utilize nomes descritivos como nomeDeUsuario, idadeDoUsuario e diaDoMes para facilitar a compreensão do código por outras pessoas que o lerem posteriormente.
Em JavaScript, é recomendado seguir regras específicas ao nomear variáveis para garantir clareza e consistência no código. Uma das regras mais amplamente aceitas é o
camelCase
, que segue o padrão de iniciar com letra minúscula e usar maiúsculas para iniciar cada palavra subsequente no nome da variável.No
camelCase
, a primeira letra da variável é minúscula, e cada palavra subsequente começa com letra maiúscula, sem espaços ou caracteres especiais entre as palavras.// Exemplos de camelCase: var meuNome; // Correto: primeira letra minúscula, iniciais de palavras subsequentes em maiúscula var minhaIdade; // Correto: primeira letra minúscula, iniciais de palavras subsequentes em maiúscula var nomeCompletoDoUsuario; // Exemplo mais descritivo usando camelCaseOBS: Você pode aprender mais lendo esse artigo: MDN - var, let, const
Não esqueça de sempre praticar; você não aprende a andar de bicicleta apenas olhando... essa frase não é sobre bicicleta.
Em JavaScript, os dados primitivos são os blocos de construção fundamentais que não são objetos e não possuem métodos ou propriedades. Cada tipo primitivo representa um único valor básico e imutável. A seguir, vamos explorar cada um dos sete tipos de dados primitivos em JavaScript, juntamente com exemplos práticos para ilustrar suas características e usos.
O tipo
string
é utilizado para representar sequências de caracteres. Strings podem conter letras, números, símbolos, espaços e outros caracteres.
- Strings são criadas usando aspas simples (
'
), aspas duplas ("
), ou crases (`
) para strings de template.let nome = "Henrique"; let corFavorita = 'Vermelho'; let frase = `Meu nome é ${nome} e minha cor favorita é ${corFavorita}`;
nome
ecorFavorita
são strings simples.frase
é uma string de template que permite interpolação de expressões.Aspas Simples (
'
) e Duplas ("
):
Utilizadas para strings simples.
Não permitem interpolação de variáveis.
Necessário escape aspas simples e duplas dentro da string (\'
,\"
).let saudacaoSimples = 'Olá, mundo!'; let saudacaoDuplas = "Olá, mundo!"; // Escaping aspas dentro da string let fraseSimples = 'Ele disse: "Olá!"'; // Aspas duplas dentro de aspas simples let fraseDuplas = "Ele disse: 'Olá!'"; // Aspas simples dentro de aspas duplas let exemploEscapingSimples = 'Isso é uma aspa simples: \''; let exemploEscapingDuplas = "Isso é uma aspa dupla: \"";Crases (
`
):
Utilizadas para template literals.
Permitem interpolação de variáveis e expressões (${}
).
Suportam strings multilinhas.let nome = "Rick"; let saudacao = `Olá, ${nome}!`; let multiline = `Esta é uma string com múltiplas linhas.`;Aspas Simples e Duplas:
Uso intercambiável, ideal para strings simples.
Não permitem interpolação direta.Crases:
Ideal para strings com interpolação de variáveis e multilinhas.
Mais flexíveis e poderosas.let nome = 'Henrique'; let saudacaoSimples = 'Olá, ' + nome + '!'; // usando concatenação let saudacaoDupla = "Olá, " + nome + "!"; // usando concatenação let saudacaoTemplate = `Olá, ${nome}!`;
O tipo
number
representa valores numéricos. JavaScript não diferencia entre inteiros e números de ponto flutuante; ambos são do tiponumber
.
- Pode representar números entre -2^53 + 1 e 2^53 - 1.
- Suporta notação exponencial para números grandes ou pequenos.
let inteiro = 42; let decimal = 3.14159; let negativo = -7; let grande = 1.23e5; // 1.23 * 10^5 = 123000
inteiro
,decimal
,negativo
egrande
são todos do tiponumber
.
O tipo
bigint
é usado para representar inteiros que estão fora do limite seguro dos números do tiponumber
.
- Criado adicionando um
n
no final do número.let muitoGrande = 1234567890123456789012345678901234567890n; let outroGrande = BigInt("9876543210987654321098765432109876543210");
muitoGrande
eoutroGrande
são do tipobigint
.
O tipo
boolean
representa um valor lógico que pode sertrue
oufalse
.
- Utilizado em condicionais e loops para controlar o fluxo do programa.
let estaChovendo = true; let estaEnsolarado = false;
estaChovendo
étrue
eestaEnsolarado
éfalse
.
O tipo
undefined
indica que uma variável foi declarada, mas ainda não foi atribuída um valor.
- O valor padrão para variáveis não inicializadas.
let valorIndefinido; // Declarando uma variavel sem valor. console.log(valorIndefinido); // undefined function naoRetornaNada() {} console.log(naoRetornaNada()); // undefined
valorIndefinido
éundefined
porque não foi inicializado.- A função
naoRetornaNada
retornaundefined
porque não possui uma instruçãoreturn
.OBS: Vamos aprender sobre funções mais adiante.
O tipo
symbol
é utilizado para criar valores únicos e imutáveis, comumente usados como identificadores de propriedades de objetos para evitar conflitos.
- Criado usando a função
Symbol
.- Cada símbolo é único, mesmo se tiverem a mesma descrição.
let simbolo1 = Symbol('descricao'); let simbolo2 = Symbol('descricao'); console.log(simbolo1 === simbolo2); // false
simbolo1
esimbolo2
são símbolos diferentes, mesmo com a mesma descrição.let usuario = { nome: "Henrique", [Symbol('id')]: 123 // 'id' é um identificador único que não conflita com outras propriedades };
O tipo
null
representa a ausência intencional de qualquer valor de objeto.
- É um valor que você pode atribuir a uma variável para indicar que ela não tem valor.
let valorNulo = null; console.log(valorNulo); // null let objeto = { chave: null }; console.log(objeto.chave); // null
valorNulo
énull
.objeto.chave
énull
indicando a ausência de um valor.
- string: Texto, como
"Olá, mundo!"
ou'Henrique'
- number: Números, como
42
ou3.14
- bigint: Números inteiros muito grandes, como
1234567890123456789012345678901234567890n
- boolean: Valores lógicos
true
oufalse
- undefined: Variável declarada, mas não inicializada, como
let valorIndefinido;
- symbol: Valores únicos e imutáveis, como
Symbol('descricao')
- null: Ausência intencional de valor, como
let valorNulo = null;
Esses tipos de dados são fundamentais para manipular informações em JavaScript de forma eficiente e eficaz, e entender suas características e comportamentos é essencial para escrever códigos robustos e bem-estruturados.
OBS: Você pode aprender mais lendo esse artigo: MDN - Tipo de Dados.
O operador
typeof
em JavaScript é usado para determinar o tipo de dado de uma variável ou expressão. Ele retorna uma string indicando o tipo do valor fornecido.Tipos Primitivos:
typeof 42; // "number" typeof "Hello"; // "string" typeof true; // "boolean" typeof undefined; // "undefined" typeof null; // "object" (nota: isso é um erro histórico em JavaScript)Objetos e Funções:
typeof { key: 'value' }; // "object" typeof [1, 2, 3]; // "object" typeof function() {}; // "function"Operadores Unários:
typeof typeof 42; // "string", pois typeof 42 retorna "number"
- Null: Embora
typeof null
retorne"object"
, isso é um erro histórico em JavaScript e não reflete o verdadeiro tipo de dadonull
.- Funções: O
typeof
pode identificar funções como"function"
, o que é útil para diferenciação em estruturas de controle ou manipulação de objetos.O
typeof
é útil em situações onde você precisa verificar dinamicamente o tipo de dado antes de realizar operações específicas que variam de acordo com o tipo.function printType(value) { console.log(typeof value); } printType(42); // "number" printType("Hello"); // "string" printType({ key: 'value' }); // "object"Isso permite que seu código tome decisões com base no tipo de dado que está manipulando, melhorando a robustez e a flexibilidade das funções.
OBS: Você pode aprender mais lendo esse artigo:MDN - Operador typeOf
Objetos em JavaScript são coleções de propriedades, e uma propriedade é uma associação entre um nome (ou chave) e um valor. Um objeto pode ser visto como uma coleção de pares chave-valor. Aqui está um resumo de como definir e usar objetos em JavaScript, com base na referência fornecida do W3Schools.
Existem várias maneiras de definir um objeto em JavaScript:
const pessoa = { primeiroNome: "Henrique", segundoNome: "Lustri", idade: 22, altura: 175 };Neste exemplo,
pessoa
é um objeto com quatro propriedades:primeiroNome
,segundoNome
,idade
, ealtura
.const pessoa = new Object(); pessoa.primeiroNome = "Henrique"; pessoa.ultimoNome = "Lustri"; pessoa.idade = 22; pessoa.altura = 175;Esta é uma abordagem mais longa e geralmente menos usada do que a notação literal.
Você pode acessar as propriedades de um objeto usando a notação de ponto (
.
) ou a notação de colchetes ([]
):const pessoa = { primeiroNome: "Henrique", segundoNome: "Lustri", idade: 22, altura: 175 }; console.log(pessoa.primeiroNome); // Saida: Henrique console.log(pessoa.segundoNome); // Saida: Lustriconst pessoa = { primeiroNome: "Henrique", segundoNome: "Lustri", idade: 22, altura: 175 }; console.log(pessoa["segundoNome"]); // Saida: Lustri console.log(pessoa["idade"]); // Saida: 22Objetos também podem conter métodos, que são funções associadas a objetos:
const pessoa = { primeiroNome: "Henrique", segundoNome: "Lustri", idade: 22, altura: 175, nomeCompleto: function() { return this.primeiroNome + " " + this.segundoNome; } }; console.log(pessoa.nomeCompleto()); // Saida: Henrique LustriNo exemplo acima,
nomeCompleto
é um método do objetopessoa
. Ele retorna o nome completo ao concatenarprimeiroNome
esegundoNome
.Você pode adicionar, modificar ou deletar propriedades de um objeto após sua criação:
pessoa.nacionalidade = "Brasileiro";pessoa.idade = 23;delete pessoa.altura;Exemplo que reúne todos esses conceitos:
const carro = { marca: "Porsche", modelo: "911 Carrera GTS", ano: 2024, descricao: function() { return this.marca + " " + this.modelo + " " + this.ano; } }; console.log(carro.descricao()); // Saida: Porsche 911 Carrera GTS 2024 carro.cor = "vermelho"; // Adiciona uma nova propriedade carro.ano = 2021; // Modifica a propriedade existente delete carro.modelo; // Deleta a propriedade
JavaScript oferece vários objetos integrados (built-in) que facilitam muitas operações comuns, sem a necessidade de criar tudo do zero. Esses objetos são parte da linguagem e estão disponíveis globalmente, o que significa que você pode usá-los diretamente em seu código.
O objeto
String
é usado para representar e manipular sequências de caracteres. Strings são imutáveis, ou seja, uma vez criadas, não podem ser alteradas diretamente, mas você pode criar novas strings baseadas nas existentes.// Criando uma string usando uma notação literal let texto = "Olá, Mundo!"; // Criando uma string usando o construtor String (não é comum) let textoObj = new String("Olá, Mundo!");// Tamanho da string console.log(texto.length); // 11 // Converte todos os caracteres para maiúsculas console.log(texto.toUpperCase()); // "OLÁ, MUNDO!" // Converte todos os caracteres para minúsculas console.log(texto.toLowerCase()); // "olá, mundo!" // Verifica se a string contém a palavra "Mundo" console.log(texto.includes("Mundo")); // true // Substitui a palavra "Mundo" por "JavaScript" console.log(texto.replace("Mundo", "JavaScript")); // "Olá, JavaScript!" // Retorna o caractere na posição 0 (primeiro caractere) console.log(texto.charAt(0)); // "O" // Extrai uma parte da string entre os índices 0 e 2 (não inclui o índice 2) console.log(texto.substring(0, 2)); // "Olá" // Exemplo: // 0 - O // 1 - l // 2 - á // 3 - , // 4 - ' ' // ... assim por diante ate mapear a string toda...O objeto
Number
é usado para representar e manipular números, incluindo inteiros e números de ponto flutuante. Ele inclui constantes e métodos úteis para trabalhar com números.// Criando um número usando uma notação literal let numero = 123; // Criando um número usando o construtor Number (não é comum) let numeroObj = new Number(123);// Converte o número para uma string console.log(numero.toString()); // "123" // Formata o número para ter 2 casas decimais console.log(numero.toFixed(2)); // "123.00" // Verifica se o valor é um número inteiro console.log(Number.isInteger(numero)); // true // Verifica se o valor é NaN (Not-a-Number) console.log(Number.isNaN(NaN)); // true // Maior valor numérico representável console.log(Number.MAX_VALUE); // 1.7976931348623157e+308 // Menor valor numérico representável console.log(Number.MIN_VALUE); // 5e-324O objeto
Math
fornece propriedades e métodos para constantes e funções matemáticas. Todos os métodos e propriedades são estáticos.// Valor de Pi (aproximado) console.log(Math.PI); // 3.141592653589793 // Raiz quadrada de 16 console.log(Math.sqrt(16)); // 4 // Número aleatório entre 0 (inclusivo) e 1 (exclusivo) console.log(Math.random()); // Pode ser qualquer valor entre 0 e 1 // Maior valor entre os números fornecidos console.log(Math.max(10, 20, 30)); // 30 // Menor valor entre os números fornecidos console.log(Math.min(10, 20, 30)); // 10 // Arredonda o número para baixo console.log(Math.floor(1.7)); // 1 // Arredonda o número para cima console.log(Math.ceil(1.2)); // 2 // Arredonda o número para o inteiro mais próximo console.log(Math.round(1.5)); // 2O objeto
Date
é usado para trabalhar com datas e horas. Ele representa um ponto no tempo e fornece métodos para obter e manipular datas e horários.// Cria um objeto Date com a data e hora atuais let dataAtual = new Date(); // Cria um objeto Date com uma data específica let dataEspecifica = new Date('2024-07-19'); // Cria um objeto Date a partir de um número de milissegundos desde 1 de janeiro de 1970 let dataMilissegundos = new Date(1626652800000);// Obtém o ano da data atual console.log(dataAtual.getFullYear()); // Ano atual (ex: 2024) // Obtém o mês da data atual (0-11, onde 0 é janeiro e 11 é dezembro) console.log(dataAtual.getMonth()); // Mês atual (ex: 6 para julho) // Obtém o dia do mês console.log(dataAtual.getDate()); // Dia do mês (ex: 19) // Obtém o dia da semana (0-6, onde 0 é domingo e 6 é sábado) console.log(dataAtual.getDay()); // Dia da semana (ex: 5 para sexta-feira) // Obtém a hora atual console.log(dataAtual.getHours()); // Hora atual (ex: 14 para 2 PM) // Obtém os minutos atuais console.log(dataAtual.getMinutes()); // Minutos atuais (ex: 30) // Obtém os segundos atuais console.log(dataAtual.getSeconds()); // Segundos atuais (ex: 45) // Converte a data para uma string em formato legível console.log(dataAtual.toLocaleDateString()); // Ex: "19/07/2024" // Obtém o número de milissegundos desde 1 de janeiro de 1970 console.log(Date.now()); // Ex: 1626652800000O objeto
Error
é usado para representar um erro que ocorreu durante a execução de um script. É útil para lançar e capturar exceções.// Cria um novo objeto Error com uma mensagem personalizada let erro = new Error("Algo deu errado!");try { // Lança o erro criado throw erro; } catch (e) { // Captura o erro e exibe o nome e a mensagem do erro console.log(e.name); // "Error" console.log(e.message); // "Algo deu errado!" }Existem tipos específicos de erros, como
TypeError
,RangeError
, eReferenceError
, que fornecem informações mais detalhadas sobre o tipo de erro que ocorreu.O objeto
Function
é usado para criar novas funções dinamicamente. Embora seja possível criar funções comFunction
, a maioria das funções é definida usando declarações ou expressões.// Cria uma nova função que soma dois números let soma = new Function('a', 'b', 'return a + b'); // Chama a função e exibe o resultado console.log(soma(2, 3)); // 5Normalmente, funções são definidas usando declarações de função ou expressões de função:
// Declaração de função function minhaFuncao(a, b) { return a + b; } // Expressão de função let minhaOutraFuncao = function(a, b) { return a + b; }; // Chama as funções console.log(minhaFuncao(2, 3)); // 5 console.log(minhaOutraFuncao(4, 5)); // 9O objeto
Boolean
é usado para representar um valor booleano (verdadeiro ou falso). Normalmente, os valores booleanos são usados diretamente sem a necessidade do construtorBoolean
.// Cria um objeto Boolean com valor verdadeiro let verdadeiro = new Boolean(true); // Cria um objeto Boolean com valor falso let falso = new Boolean(false);Normalmente, valores booleanos são usados diretamente:
// Declara variáveis booleanas diretamente let isTrue = true; let isFalse = false; // Verifica o valor das variáveis booleanas console.log(isTrue); // true console.log(isFalse); // false
Em JavaScript, prototypes são um mecanismo que permite a criação de novos objetos a partir de outros objetos. Cada objeto tem uma referência interna para outro objeto chamado de prototype. Esta referência é acessível via
Object.getPrototypeOf(obj)
eobj.__proto__
(embora o uso de__proto__
seja desencorajado).Quando você tenta acessar uma propriedade de um objeto, o JavaScript verifica primeiro se essa propriedade existe no próprio objeto. Se não encontrar, ele procura na cadeia de protótipos. Esta cadeia é uma sequência de objetos conectados por suas referências de prototype.
// Definindo uma função construtora function Pessoa(nome) { this.nome = nome; // Propriedade 'nome' definida diretamente no objeto } // Adicionando um método ao prototype de Pessoa Pessoa.prototype.falarOla = function() { console.log(`Olá, meu nome é ${this.nome}`); // 'this.nome' acessa a propriedade do objeto }; // Criando uma nova instância de Pessoa const henrique = new Pessoa('Henrique'); // Chamando o método 'falarOla' da instância 'henrique' henrique.falarOla(); // Saida: Olá, meu nome é Henrique
- Função Construtora:
Pessoa
é uma função construtora que cria objetos com uma propriedadenome
.- Método no Prototype: O método
falarOla
é adicionado ao prototype da funçãoPessoa
. Assim, todas as instâncias dePessoa
terão acesso a esse método.- Instância e Acesso ao Método: Quando criamos
henrique
usandonew Pessoa('Henrique')
, ele herda o métodofalarOla
do prototype. Isso permite que chamemoshenrique.falarOla()
e acesse a propriedadenome
definida na instância.
Prototypal Inheritance é o modelo de herança em JavaScript onde objetos podem herdar diretamente de outros objetos. Em vez de definir uma classe base e subclasses, você pode criar um objeto a partir de outro objeto, permitindo que o novo objeto herde as propriedades e métodos do objeto original.
// Criando um objeto 'animal' com uma propriedade 'comem' const animal = { comem: true // Todos os animais "comem" }; // Criando um novo objeto 'coelho' que herda de 'animal' const coelho = Object.create(animal); // 'coelho' herda de 'animal' coelho.pular = true; // 'coelho' tem uma propriedade própria 'pular' // Acessando propriedades do objeto 'coelho' console.log(coelho.comem); // Saida: true (herdado de 'animal') console.log(coelho.pular); // Saida: true (propriedade própria de 'coelho') // Demonstrando a cadeia de protótipos console.log(Object.getPrototypeOf(coelho) === animal); // Saida: true (verifica se 'animal' é o >> protótipo de 'coelho')
- Objeto Base:
animal
é um objeto com uma propriedadecomem
. Esse objeto serve como protótipo.- Criação do Objeto Herdeiro: Usamos
Object.create(animal)
para criarcoelho
. Este método cria um novo objeto que herda diretamente deanimal
.- Propriedades e Métodos:
coelho
pode acessar a propriedadecomem
herdada deanimal
. Além disso,coelho
tem uma propriedade própria chamadapular
.- Verificação da Cadeia de Protótipos:
Object.getPrototypeOf(coelho)
retorna o objeto de quecoelho
herda, que éanimal
. Isso confirma a cadeia de protótipos.
- Prototypes em JavaScript permitem que objetos compartilhem métodos e propriedades, economizando memória e promovendo a reutilização de código.
- Prototypal Inheritance permite criar objetos que herdam diretamente de outros objetos, criando uma cadeia onde propriedades e métodos podem ser acessados e compartilhados entre objetos.
OBS: Você pode aprender mais lendo esse artigo: MDN - Objetos e MDN - prototypes e prototypal inheritance
Não esqueça de sempre praticar; você não aprende a andar de skate apenas olhando... essa frase não é sobre skate.
Type Casting refere-se ao processo de transformar um valor de um tipo de dado para outro tipo. Em JavaScript, isso pode ser feito de forma explícita ou implícita.
Explicit Type Casting (ou casting explicit) ocorre quando você, como desenvolvedor, toma medidas específicas para converter um tipo de dado para outro. Isso é feito usando funções e métodos específicos para fazer a conversão.
Convertendo para Número:
let texto = "123"; // Declaração de uma string com valor "123" let numero = Number(texto); // Converte a string para número usando a função Number console.log(numero); // 123 - Saída do número convertido console.log(typeof numero); // "number" - Saída do tipo do valor convertidoConvertendo para String:
let numero = 123; // Declaração de um número let texto = String(numero); // Converte o número para string usando a função String console.log(texto); // "123" - Saída da string convertida console.log(typeof texto); // "string" - Saída do tipo do valor convertidoConvertendo para Booleano:
let valor = 0; // Declaração de um número com valor 0 let bool = Boolean(valor); // Converte o número para booleano usando a função Boolean console.log(bool); // false - Saída do booleano convertido console.log(typeof bool); // "boolean" - Saída do tipo do valor convertido
Implicit Type Casting (ou casting implicit) ocorre quando o JavaScript automaticamente converte tipos de dados conforme necessário. Isso geralmente acontece em operações que envolvem diferentes tipos de dados.
Concatenação de Strings:
let numero = 123; // Declaração de um número let texto = "O número é " + numero; // Concatena número com string, convertendo implicitamente o número para string console.log(texto); // "O número é 123" - Saída da string concatenada console.log(typeof texto); // "string" - Saída do tipo do valor resultanteOperações Aritméticas:
let texto = "5"; // Declaração de uma string com valor "5" let numero = 10; // Declaração de um número com valor 10 let resultado = texto * numero; // Multiplica string por número, convertendo implicitamente a string para número console.log(resultado); // 50 - Saída do resultado da multiplicação console.log(typeof resultado); // "number" - Saída do tipo do valor resultanteContexto Booleano:
let valor = "Olá"; // Declaração de uma string com valor "hello" if (valor) { // Converte a string para booleano implicitamente console.log("A string não está vazia!"); // "A string não está vazia!" - Saída caso a string seja verdadeira }
Type Conversion (ou Type Coercion) refere-se ao mesmo conceito de Type Casting, onde um tipo de dado é convertido em outro. Em JavaScript, a conversão de tipo pode ser:
- Automática (implícita): Quando o JavaScript automaticamente converte o tipo de dado conforme necessário, como mostrado nos exemplos de casting implícito.
- Manual (explícita): Quando o desenvolvedor usa métodos e funções específicas para converter os tipos de dados, como mostrado nos exemplos de casting explícito.
A conversão de tipo pode ocorrer em diferentes contextos, como em operações aritméticas, comparação de valores, e contextos booleanos.
Comparação de Valores:
console.log("5" == 5); // true - Converte string para número implicitamente e compara os valores console.log("5" === 5); // false - Sem conversão implícita, compara tipos diferentes (string e número)Operações Aritméticas com Strings:
let resultado = "10" - 2; // Converte string para número implicitamente e realiza a subtração console.log(resultado); // 8 - Saída do resultado da subtração console.log(typeof resultado); // "number" - Saída do tipo do valor resultanteConversão em Contexto Booleano:
let valor = ""; // Declaração de uma string vazia if (valor) { // Converte a string vazia para false implicitamente console.log("Esta mensagem não será exibida"); } else { console.log("String está vazia"); // "String está vazia" - Saída caso a string seja falsa }
Não esqueça de sempre praticar; você não aprende a andar de patinete apenas olhando... essa frase não é sobre patinete.
As estruturas de dados são formas organizadas de armazenar e manipular informações em um computador. Elas são fundamentais para a eficiência dos algoritmos e do desempenho dos programas. Vamos explorar algumas das principais categorias de estruturas de dados em detalhes.
Indexed collections são estruturas de dados onde os elementos são acessados através de índices numéricos. Dois exemplos comuns são matrizes e matrizes tipadas.
Uma matriz (ou array) é uma coleção de elementos, geralmente do mesmo tipo, armazenados em posições consecutivas na memória. Os elementos são acessados por índices, que começam em 0.
Criação de um Array:
Você pode criar um array usando colchetes[]
e separando os elementos por vírgulas.let frutas = ["Maçã", "Banana", "Laranja"];Acessando Elementos:
Os elementos são acessados usando o índice do array, que começa em 0.console.log(frutas[0]); // Saída: Maçã console.log(frutas[1]); // Saída: Banana console.log(frutas[2]); // Saída: LaranjaModificando Elementos:
Você pode modificar os elementos do array atribuindo novos valores aos índices correspondentes.frutas[1] = "Morango"; console.log(frutas[1]); // Saída: MorangoMétodos Comuns:
Arrays vêm com diversos métodos embutidos para manipulação.// Adiciona um elemento ao final frutas.push("Uva"); console.log(frutas); // Saída: ["Maçã", "Morango", "Laranja", "Uva"] // Remove o último elemento frutas.pop(); console.log(frutas); // Saída: ["Maçã", "Morango", "Laranja"] // Itera sobre os elementos frutas.forEach(function(fruta) { console.log(fruta); }); // Saída: // Maçã // Morango // Laranja // Adiciona um elemento ao início frutas.unshift("Pera"); console.log(frutas); // Saída: ["Pera", "Maçã", "Morango", "Laranja"] // Remove o primeiro elemento frutas.shift(); console.log(frutas); // Saída: ["Maçã", "Morango", "Laranja"] // Encontra o índice de um elemento let indice = frutas.indexOf("Morango"); console.log(indice); // Saída: 1 // Remove um elemento específico let indiceBanana = frutas.indexOf("Banana"); if (indiceBanana !== -1) { frutas.splice(indiceBanana, 1); } console.log(frutas); // Saída: ["Maçã", "Morango", "Laranja"] // Copia uma parte do array let algumasFrutas = frutas.slice(1, 3); console.log(algumasFrutas); // Saída: ["Morango", "Laranja"] // Mescla dois arrays let maisFrutas = ["Abacaxi", "Manga"]; let todasFrutas = frutas.concat(maisFrutas); console.log(todasFrutas); // Saída: ["Maçã", "Morango", "Laranja", "Abacaxi", "Manga"]Matrizes tipadas são arrays que armazenam dados de um único tipo. Elas são úteis para operações de baixo nível e manipulação de grandes volumes de dados numéricos.
Criação de um Typed Array:
Você cria um Typed Array usando construtores comoInt32Array
,Float64Array
, etc.let buffer = new ArrayBuffer(16); // Cria um buffer de 16 bytes let int32View = new Int32Array(buffer); // Cria uma visão Int32Array sobre o buffer int32View[0] = 42; int32View[1] = 43; console.log(int32View[0]); // Saída: 42 console.log(int32View[1]); // Saída: 43Iterando sobre um Typed Array:
Você pode usar um loop para iterar sobre os elementos de um Typed Array.for (let i = 0; i < int32View.length; i++) { console.log(int32View[i]); } // Saída: // 42 // 43 // 0 // 0
Keyed Collections armazenam dados em pares chave-valor, permitindo acesso eficiente aos valores através de suas chaves.
Um Map é uma coleção de pares chave-valor onde as chaves podem ser de qualquer tipo.
Criação de um Map:
Você cria um Map usando o construtorMap
.let mapa = new Map();Adicionando e Acessando Elementos:
Useset
para adicionar elementos eget
para acessá-los.mapa.set("nome", "Henrique"); mapa.set("idade", 22); console.log(mapa.get("nome")); // Saída: Henrique console.log(mapa.get("idade")); // Saída: 22Verificando a Existência de uma Chave:
Usehas
para verificar se uma chave existe no Map.console.log(mapa.has("nome")); // Saída: true console.log(mapa.has("endereço")); // Saída: falseRemovendo Elementos:
Usedelete
para remover um elemento por sua chave.mapa.delete("idade"); console.log(mapa.has("idade")); // Saída: falseIterando sobre um Map:
Você pode usarforEach
oufor...of
para iterar sobre os elementos do Map.mapa.forEach((valor, chave) => { console.log(`${chave}: ${valor}`); }); // Saída: // nome: Henrique for (let [chave, valor] of mapa) { console.log(`${chave}: ${valor}`); } // Saída: // nome: HenriqueUm WeakMap é similar a um Map, mas só aceita objetos como chaves e não impede que esses objetos sejam coletados pelo garbage collector se não houver outras referências a eles.
Criação de um WeakMap:
Use o construtorWeakMap
.let weakMap = new WeakMap();Adicionando e Acessando Elementos:
Useset
para adicionar elementos eget
para acessá-los.let obj = {nome: "Henrique"}; weakMap.set(obj, "algum valor"); console.log(weakMap.get(obj)); // Saída: algum valorGarbage Collection:
Quando a referência ao objeto é removida, ele pode ser coletado pelo garbage collector.obj = null; // O objeto agora é elegível para garbage collectionUm Set é uma coleção de valores únicos, ou seja, não permite elementos duplicados.
Criação de um Set:
Use o construtorSet
.let conjunto = new Set();Adicionando Elementos:
Useadd
para adicionar elementos.conjunto.add(1); conjunto.add(2); conjunto.add(2); // Ignorado, pois 2 já está no conjuntoVerificando a Existência de um Elemento:
Usehas
para verificar se um elemento existe no Set.console.log(conjunto.has(1)); // Saída: true console.log(conjunto.has(3)); // Saída: falseRemovendo Elementos:
Usedelete
para remover um elemento.conjunto.delete(1); console.log(conjunto.has(1)); // Saída: falseIterando sobre um Set:
Você pode usarforEach
oufor...of
para iterar sobre os elementos do Set.conjunto.forEach((valor) => { console.log(valor); }); // Saída: // 2 for (let valor of conjunto) { console.log(valor); } // Saída: // 2Um WeakSet é similar a um Set, mas só armazena objetos e permite que esses objetos sejam coletados pelo garbage collector se não houver outras referências a eles.
Criação de um WeakSet:
Use o construtorWeakSet
.let weakSet = new WeakSet();Adicionando Elementos:
Useadd
para adicionar elementos.let obj1 = {data: "dados"}; weakSet.add(obj1);Garbage Collection:
Quando a referência ao objeto é removida, ele pode ser coletado pelo garbage collector.obj1 = null; // O objeto agora é elegível para garbage collection
Structured data são usados para representar e trocar dados de maneira estruturada e legível.
JSON (JavaScript Object Notation) é um formato leve de troca de dados, fácil de ler e escrever para humanos e fácil de analisar e gerar para máquinas.
Objeto JavaScript:
Um objeto em JavaScript pode ser convertido para JSON.let pessoa = { nome: "Henrique", idade: 22, cidade: "São Paulo" };Conversão para JSON:
UseJSON.stringify
para converter um objeto JavaScript para uma string JSON.let json = JSON.stringify(pessoa); console.log(json); // Saída: {"nome":"Henrique","idade":22,"cidade":"São Paulo"}Conversão de JSON para Objeto:
UseJSON.parse
para converter uma string JSON de volta para um objeto JavaScript.let obj = JSON.parse(json); console.log(obj.nome); // Saída: HenriqueOBS: Você pode aprender mais lendo esse artigo: MDN - Arrays, MDN - Typed Arrays, MDN - Map, MDN - WeakMap, MDN - Set, MDN - WeakSet, MDN - JSON
Não esqueça de sempre praticar; você não aprende a programar apenas olhando... essa sim! essa frase é sobre programar.
Comparações de igualdade são fundamentais em JavaScript para verificar se dois valores são iguais. Existem diferentes operadores e algoritmos para fazer essas comparações, cada um com suas particularidades. Vamos explorar os principais operadores de comparação de valor e os algoritmos de igualdade em detalhes.
O operador
==
compara dois valores para igualdade, realizando conversão de tipo (type coercion) se necessário. Isso significa que ele tenta converter os operandos para um tipo comum antes de compará-los.console.log(1 == '1'); // true // O valor numérico 1 é comparado com o valor da string '1'. // A string é convertida para o número 1 antes da comparação, resultando em true. console.log(true == 1); // true // O valor booleano true é comparado com o valor númerico 1. // true é convertido para 1 antes da comparação, resultando em true. console.log(null == undefined); // true // null e undefined são considerados iguais pelo operador ==O operador
===
compara dois valores para igualdade sem realizar conversão de tipo. Ambos os valores e tipos devem ser iguais para que a comparação seja verdadeira.console.log(1 === '1'); // false // O valor numérico 1 é comparado com a string '1'. // Como os tipos são diferentes (número e string), o resultado é false. console.log(true === 1); // false // O valor booleano true é comparado com o número 1. // Como os tipos são diferentes (booleano e número), o resultado é false. console.log(null === undefined); // false // null e undefined têm tipos diferentes, resultando em false.O método
Object.is()
compara dois valores para igualdade. Ele é semelhante ao operador===
, mas trataNaN
como igual aNaN
e diferencia+0
de-0
.console.log(Object.is(1, 1)); // true // Ambos os valores e tipos são iguais. console.log(Object.is(NaN, NaN)); // true // Diferente de `===`, `Object.is()` considera NaN igual a NaN. console.log(Object.is(+0, -0)); // false // `Object.is()` diferencia +0 e -0, ao contrário de `===`.
O algoritmo de igualdade abstrata é usado pelo operador
==
para comparar dois valores com coerção de tipo.\
- Se um valor é
null
e o outro éundefined
, eles são iguais.- Se um valor é um número e o outro é uma string, converta a string para número e compare.
- Se um valor é booleano, converta-o para número (
true
se torna1
,false
se torna0
) e compare.- Se um valor é um objeto e o outro é um tipo primitivo, converta o objeto para seu valor primitivo usando a função
toPrimitive
.console.log('42' == 42); // true // A string '42' é convertida para o número 42 antes da comparação, resultando em true. console.log(true == 1); // true // O booleano true é convertido para o número 1 antes da comparação, resultando em true. console.log([1, 2] == '1,2'); // true // O array [1, 2] é convertido para a string '1,2' antes da comparação, resultando em true. console.log({} == '[object Object]'); // false // O objeto {} não é convertido para a string '[object Object]', então a comparação resulta em false.Este algoritmo é usado pelo operador
===
e é mais simples, pois não realiza conversões de tipo. Ele apenas verifica se os dois valores são do mesmo tipo e valor.console.log('42' === 42); // false // A string '42' e o número 42 são de tipos diferentes, então a comparação resulta em false. console.log(true === 1); // false // O booleano true e o número 1 são de tipos diferentes, então a comparação resulta em false. console.log([1, 2] === '1,2'); // false // O array [1, 2] e a string '1,2' são de tipos diferentes, então a comparação resulta em false. console.log({} === {}); // false // Cada objeto {} é uma instância diferente na memória, então a comparação resulta em false. console.log(NaN === NaN); // false // NaN nunca é igual a si mesmo, então a comparação resulta em false. console.log(+0 === -0); // true // 0 positivo e negativo são considerados iguais, então a comparação resulta em true. console.log(Symbol('foo') === Symbol('foo')); // false // Cada chamada a Symbol() cria um símbolo único, então a comparação resulta em false.O algoritmo
SameValueZero
consideraNaN
igual aNaN
e não diferencia entre+0
e-0
. É usado em algumas operações de array e objetos.console.log(Object.is(NaN, NaN)); // true // `Object.is()` usa o algoritmo `SameValueZero`, então NaN é igual a NaN. console.log([+0].includes(-0)); // true // O método `Array.prototype.includes` usa `SameValueZero`, então +0 é igual a -0.O algoritmo
SameValue
é similar aoStrictEqual
, mas consideraNaN
igual aNaN
e diferencia+0
e-0
.console.log(Object.is(+0, -0)); // false // `Object.is()` usa o algoritmo `SameValue`, diferenciando +0 e -0. console.log(Object.is(NaN, NaN)); // true // `SameValue` considera NaN igual a NaN.Entender os diferentes operadores de comparação e algoritmos de igualdade em JavaScript é essencial para escrever código correto e previsível. Saber quando usar
==
,===
ouObject.is()
, e como os algoritmosLooselyEqual
,StrictlyEqual
,SameValueZero
eSameValue
funcionam, ajuda a evitar bugs e comportamentos inesperados no código.OBS: Você pode aprender mais lendo esse artigo: w3schools - Comparações e MDN - Comparação de Igualdade
Não esqueça de sempre praticar; você não aprende a jogar bola apenas olhando... essa frase não é sobre jogar bola.
O loop
for
é uma estrutura de repetição que permite executar um bloco de código um número específico de vezes. Ele é útil quando você sabe com antecedência quantas vezes deseja repetir uma operação.for (inicialização; condição; incremento) { // Código a ser executado }
- Inicialização: Definição e inicialização de uma variável de controle (geralmente um contador).
- Condição: A condição que será verificada antes de cada iteração do loop. Se a condição for verdadeira, o bloco de código dentro do loop será executado.
- Incremento: Atualização da variável de controle após cada iteração.
for (let i = 0; i < 5; i++) { console.log("O número é " + i); }
let i = 0;
- Inicializa a variáveli
com 0.i < 5;
- O loop continua enquantoi
for menor que 5.i++;
- Incrementai
em 1 após cada iteração.- Imprime os números de 0 a 4 no console.
A declaração
for...of
é usada para iterar sobre objetos iteráveis (como arrays, strings, Map, Set, etc.), permitindo acessar diretamente os valores dos elementos.for (const valor of iterável) { // Código a ser executado }
const valor
- Declara uma variável que armazena o valor do elemento atual.iterável
- O objeto iterável (como um array) sobre o qual você está iterando.const array = ['a', 'b', 'c', 'd']; for (const elemento of array) { console.log(elemento); }
- Itera sobre cada elemento do array
array
.- Imprime
a
,b
,c
ed
no console.A declaração
for...in
é usada para iterar sobre as propriedades enumeráveis de um objeto, permitindo acessar as chaves dessas propriedades.for (const propriedade in objeto) { // Código a ser executado }
const propriedade
- Declara uma variável que armazena a chave da propriedade atual.objeto
- O objeto cujas propriedades você está iterando.const objeto = {a: 1, b: 2, c: 3}; for (const chave in objeto) { console.log(`A chave é ${chave} e o valor é ${objeto[chave]}`); }
- Itera sobre cada chave do objeto
objeto
.- Imprime
A chave é a e o valor é 1
,A chave é b e o valor é 2
, eA chave é c e o valor é 3
no console.
O loop
while
executa um bloco de código enquanto uma condição especificada for verdadeira. É usado quando não se sabe antecipadamente quantas vezes o loop deve ser executado.while (condição) { // Código a ser executado }
condição
- A condição que será verificada antes de cada iteração do loop. Se a condição for verdadeira, o bloco de código será executado.let contador = 0; while (contador < 5) { console.log("O contador é " + contador); contador++; }
- Executa o loop enquanto
contador
for menor que 5.- Incrementa
contador
em 1 após cada iteração.- Imprime os números de 0 a 4 no console.
O loop
do...while
é semelhante aowhile
, mas garante que o bloco de código seja executado pelo menos uma vez, pois a condição é verificada após a execução do bloco de código.do { // Código a ser executado } while (condição);
- O bloco de código dentro do
do
será executado primeiro, independentemente da condição.- A condição é verificada após a execução do bloco de código.
let contador = 0; do { console.log("O contador é " + contador); contador++; } while (contador < 5);
- Executa o bloco de código e depois verifica a condição.
- Isso garante que o código seja executado pelo menos uma vez, mesmo que a condição inicial não seja verdadeira.
- Imprime os números de 0 a 4 no console.
As declarações
break
econtinue
são usadas para controlar o fluxo dos loops.
break
: Sai imediatamente do loop, interrompendo sua execução.continue
: Pula a iteração atual e vai para a próxima iteração do loop.for (let i = 0; i < 10; i++) { if (i === 5) { break; } console.log(i); }
- O loop será interrompido quando
i
for igual a 5.- O console mostrará
0, 1, 2, 3, 4
.for (let i = 0; i < 10; i++) { if (i === 5) { continue; } console.log(i); }
- O loop pula a iteração quando
i
é igual a 5.- O console mostrará
0, 1, 2, 3, 4, 6, 7, 8, 9
.As declarações rotuladas são usadas para identificar um loop com um rótulo, permitindo que
break
econtinue
saiam de um loop específico, especialmente útil em loops aninhados.rotulo: { // Código a ser executado }externo: for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { if (i === 1 && j === 1) { break externo; } console.log(`i = ${i}, j = ${j}`); } }
- O loop externo será interrompido quando
i
for igual a 1 ej
for igual a 1.- O console mostrará
i = 0, j = 0
,i = 0, j = 1
,i = 0, j = 2
,i = 1, j = 0
.OBS: Você pode aprender mais lendo esse artigo: MDN - loops e iterações
O controle de fluxo determina a ordem na qual as instruções do seu código são executadas. Ele permite que você execute diferentes blocos de código dependendo das condições que você define. Vamos explorar os principais conceitos: condicionais e manipulação de exceções.
Os condicionais são estruturas que permitem executar diferentes blocos de código com base em uma condição. Os principais tipos de condicionais são
if
,if...else
,else if...else
, eswitch
.A estrutura básica do
if
permite executar um bloco de código se uma condição for verdadeira.let idade = 18; // Declarando uma variável idade com o valor 18 // Verificando se a idade é maior ou igual a 18 if (idade >= 18) { console.log("Você é maior de idade."); // Será exibido no console se a condição for verdadeira } // Saída: Você é maior de idade.Se
idade
é maior ou igual a 18, a mensagem "Você é maior de idade." é exibida no console.O
if...else
permite executar um bloco de código se a condição for verdadeira e outro bloco se a condição for falsa.let idade = 16; // Declarando uma variável idade com o valor 16 // Verificando se a idade é maior ou igual a 18 if (idade >= 18) { console.log("Você é maior de idade."); // Será exibido no console se a condição for verdadeira } else { console.log("Você é menor de idade."); // Será exibido no console se a condição for falsa } // Saída: Você é menor de idade.Se
idade
for menor que 18, o bloco dentro doelse
será executado.O
else if...else
é útil quando você tem múltiplas condições a serem verificadas.let nota = 85; // Declarando uma variável nota com o valor 85 // Verificando a faixa de valores da nota if (nota >= 90) { console.log("Nota A"); // Será exibido se nota for maior ou igual a 90 } else if (nota >= 80) { console.log("Nota B"); // Será exibido se nota for maior ou igual a 80 e menor que 90 } else if (nota >= 70) { console.log("Nota C"); // Será exibido se nota for maior ou igual a 70 e menor que 80 } else { console.log("Nota abaixo de C"); // Será exibido se nota for menor que 70 } // Saída: Nota BA primeira condição que for verdadeira determina qual bloco de código será executado. Neste caso,
nota
é 85, então "Nota B" é exibida.O
switch
é uma alternativa aoif...else
quando você tem várias condições baseadas em uma única variável.let dia = 3; // Declarando uma variável dia com o valor 3 // Verificando o valor da variável dia switch (dia) { case 1: console.log("Domingo"); // Será exibido se dia for igual a 1 break; case 2: console.log("Segunda-feira"); // Será exibido se dia for igual a 2 break; case 3: console.log("Terça-feira"); // Será exibido se dia for igual a 3 break; default: console.log("Dia inválido"); // Será exibido se dia não for igual a nenhum dos casos acima } // Saída: Terça-feiraO
switch
compara o valor da variáveldia
com cadacase
. Quando encontra uma correspondência (3), o código dentro dessecase
é executado até encontrar umbreak
.
A manipulação de exceções permite lidar com erros que ocorrem durante a execução do código. Os principais componentes são
throw
,try
,catch
,finally
, e objetos de erro.O
throw
é usado para lançar uma exceção quando você deseja sinalizar que algo deu errado.// Função para verificar a idade function verificarIdade(idade) { if (idade < 0) { throw new Error("Idade não pode ser negativa"); // Lançando um erro se a idade for negativa } console.log("Idade válida"); // Será exibido se a idade for válida } try { verificarIdade(-5); // Chamando a função com uma idade negativa } catch (error) { console.log(error.message); // Capturando e exibindo a mensagem de erro } // Saída: Idade não pode ser negativaSe a
idade
for negativa, um erro é lançado com a mensagem "Idade não pode ser negativa". O blocotry...catch
captura esse erro.O
try...catch
é usado para capturar e tratar erros que ocorrem dentro do blocotry
.try { let resultado = 10 / 0; // Tentativa de divisão por zero, mas não gera erro em JavaScript, apenas Infinity console.log(resultado); // Exibirá Infinity } catch (error) { console.log("Ocorreu um erro: " + error.message); // Não será executado neste caso } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Infinity // Bloco finally executado.O código dentro do
try
é executado. Se um erro ocorrer, o blococatch
captura o erro e você pode tratá-lo conforme necessário. Ofinally
é sempre executado, independentemente de um erro ocorrer ou não.O bloco
finally
é opcional e é sempre executado após o blocotry
, independentemente de um erro ter ocorrido ou não.try { console.log("Tentando executar o código..."); // Exibindo uma mensagem throw new Error("Erro intencional"); // Lançando um erro intencionalmente } catch (error) { console.log("Erro capturado: " + error.message); // Capturando e exibindo a mensagem de erro } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Tentando executar o código... // Erro capturado: Erro intencional // Bloco finally executado.O
finally
é útil para garantir que certas operações, como fechar arquivos ou liberar recursos, sejam sempre realizadas, independentemente de o código ter executado com sucesso ou ter lançado um erro.
Os objetos de erro são usados para criar instâncias de erros em JavaScript. Eles fornecem informações detalhadas sobre o erro ocorrido.
try { throw new Error("Erro genérico"); // Lançando um erro genérico } catch (error) { console.log("Nome do erro: " + error.name); // Exibindo o nome do erro console.log("Mensagem do erro: " + error.message); // Exibindo a mensagem do erro } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Nome do erro: Error // Mensagem do erro: Erro genérico // Bloco finally executado.
Error
é o tipo básico de erro em JavaScript. Ele é utilizado para criar erros genéricos. A mensagem do erro é "Erro genérico".try { null.f(); // Tentativa de chamar um método em null, o que gera um TypeError } catch (error) { console.log("Nome do erro: " + error.name); // Exibindo o nome do erro console.log("Mensagem do erro: " + error.message); // Exibindo a mensagem do erro } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Nome do erro: TypeError // Mensagem do erro: Cannot read property 'f' of null // Bloco finally executado.
TypeError
ocorre quando um valor é utilizado de uma maneira incompatível com seu tipo. Aqui, tenta-se acessar a propriedadef
denull
, que não é um objeto.try { console.log(x); // x não está definido, o que gera um ReferenceError } catch (error) { console.log("Nome do erro: " + error.name); // Exibindo o nome do erro console.log("Mensagem do erro: " + error.message); // Exibindo a mensagem do erro } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Nome do erro: ReferenceError // Mensagem do erro: x is not defined // Bloco finally executado.
ReferenceError
ocorre quando se tenta acessar uma variável que não está definida. Aqui,x
não foi declarado anteriormente.try { let x = 1; x.toPrecision(500); // Tentativa de usar um valor fora do intervalo permitido, o que gera um RangeError } catch (error) { console.log("Nome do erro: " + error.name); // Exibindo o nome do erro console.log("Mensagem do erro: " + error.message); // Exibindo a mensagem do erro } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Nome do erro: RangeError // Mensagem do erro: toPrecision() argument must be between 1 and 100 // Bloco finally executado.
RangeError
ocorre quando um valor não está dentro do intervalo ou conjunto de valores permitidos. Aqui, o argumento paratoPrecision
está fora do intervalo aceitável (1 a 100).try { eval('foo bar'); // Tentativa de executar uma sintaxe inválida, o que gera um SyntaxError } catch (error) { console.log("Nome do erro: " + error.name); // Exibindo o nome do erro console.log("Mensagem do erro: " + error.message); // Exibindo a mensagem do erro } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Nome do erro: SyntaxError // Mensagem do erro: Unexpected identifier // Bloco finally executado.
SyntaxError
ocorre quando o código não é sintaticamente válido. Aqui,eval
tenta executar um código que não é uma expressão JavaScript válida.try { decodeURIComponent('%'); // Tentativa de decodificar uma URI inválida, o que gera um URIError } catch (error) { console.log("Nome do erro: " + error.name); // Exibindo o nome do erro console.log("Mensagem do erro: " + error.message); // Exibindo a mensagem do erro } finally { console.log("Bloco finally executado."); // Será sempre executado } // Saída: // Nome do erro: URIError // Mensagem do erro: URI malformed // Bloco finally executado.
URIError
ocorre quando as funções globaisencodeURI
,decodeURI
,encodeURIComponent
, oudecodeURIComponent
recebem uma URI malformada. Aqui,%
não é um componente válido.OBS: Você pode aprender mais lendo esse artigo: MDN - if...else, MDN - switch, MDN - try...catch, MDN - Error Objects
Em JavaScript, expressões são combinações de valores, variáveis e operadores que são avaliadas para produzir um resultado. Uma expressão pode ser tão simples quanto um número ou uma string, ou tão complexa quanto uma função invocada com argumentos e operadores aninhados.
Operadores são símbolos ou palavras-chave que indicam ao JavaScript para realizar operações específicas sobre os operandos. Eles permitem manipular dados e variáveis para realizar cálculos, comparar valores, executar operações lógicas, entre outras tarefas. Os operadores são fundamentais para construir expressões que compõem a lógica do código.
Operadores Aritméticos: Realizam operações matemáticas básicas como adição, subtração, multiplicação, divisão e mais.
Operadores de Comparação: Comparam valores e retornam um booleano (verdadeiro ou falso).
Operadores Lógicos: Combinações de expressões booleanas.
Operadores Condicionais: Avaliam uma condição e retornam um valor baseado na condição.
Operadores Unários: Operam em um único operando para produzir um novo valor.
Operadores Bit a Bit: Operam diretamente sobre os bits dos operandos.
Operadores de Atribuição: Atribuem valores às variáveis e podem incluir operações aritméticas.Os operadores aritméticos realizam operações matemáticas básicas, como adição, subtração, multiplicação, divisão e outras operações relacionadas. Eles são essenciais para manipular números e realizar cálculos dentro do código.
let a = 5; let b = 3; console.log(a + b); // Saída: 8 // Aqui, `5 + 3` resulta em `8`.console.log(a - b); // Saída: 2 // Aqui, `5 - 3` resulta em `2`.console.log(a * b); // Saída: 15 // Aqui, `5 * 3` resulta em `15`.console.log(a / b); // Saída: 1.6667 // Aqui, `5 / 3` resulta em aproximadamente `1.6667`.console.log(a % b); // Saída: 2 // Aqui, `5 % 3` resulta em `2` porque 5 dividido por 3 dá 1 com um resto de 2.console.log(a ** b); // Saída: 125 // Aqui, `5 ** 3` (5 elevado a 3) resulta em `125`.
Os operadores de atribuição são usados para atribuir valores às variáveis. Eles podem incluir operações aritméticas junto com a atribuição, como adicionar e atualizar uma variável com um novo valor (+=, -=, *=).
let x = 10; x += 5; // Equivale a x = x + 5 console.log(x); // Saída: 15 // Aqui, `x` inicialmente vale `10`. `x += 5` faz `x` se tornar `15`.x -= 3; // Equivale a x = x - 3 console.log(x); // Saída: 12 // Aqui, `x` inicialmente vale `15`. `x -= 3` faz `x` se tornar `12`.x *= 2; // Equivale a x = x * 2 console.log(x); // Saída: 24 // Aqui, `x` inicialmente vale `12`. `x *= 2` faz `x` se tornar `24`.x /= 4; // Equivale a x = x / 4 console.log(x); // Saída: 6 // Aqui, `x` inicialmente vale `24`. `x /= 4` faz `x` se tornar `6`.x %= 4; // Equivale a x = x % 4 console.log(x); // Saída: 2 // Aqui, `x` inicialmente vale `6`. `x %= 4` faz `x` se tornar `2`.x **= 2; // Equivale a x = x ** 2 console.log(x); // Saída: 4 // Aqui, `x` inicialmente vale `2`. `x **= 2` faz `x` se tornar `4`.
Os operadores de comparação comparam dois valores e retornam um resultado booleano, true ou false, dependendo se a comparação é verdadeira ou falsa. Esses operadores são úteis para tomar decisões com base na comparação de valores.
console.log(10 > 5); // Saída: true // Aqui, `10 > 5` é verdadeiro, então a saída é `true`.console.log(10 < 5); // Saída: false // Aqui, `10 < 5` é falso, então a saída é `false`.console.log(10 >= 5); // Saída: true // Aqui, `10 >= 5` é verdadeiro, então a saída é `true`.console.log(10 <= 5); // Saída: false // Aqui, `10 <= 5` é falso, então a saída é `false`.console.log(5 == "5"); // Saída: true // Aqui, `5 == "5"` é verdadeiro porque o operador `==` não verifica o tipo.console.log(5 === "5"); // Saída: false // Aqui, `5 === "5"` é falso porque o tipo `number` não é igual ao tipo `string`.console.log(5 != "5"); // Saída: false // Aqui, `5 != "5"` é falso porque o operador `!=` não verifica o tipo.console.log(5 !== "5"); // Saída: true // Aqui, `5 !== "5"` é verdadeiro porque o tipo `number` não é igual ao tipo `string`.
Os operadores lógicos permitem combinar várias expressões booleanas em uma única expressão complexa. Eles retornam um resultado booleano e são usados para testar múltiplas condições ao mesmo tempo.
console.log(true || false); // Saída: true // Aqui, `true || false` é verdadeiro porque pelo menos uma das expressões é verdadeira.console.log(true && false); // Saída: false // Aqui, `true && false` é falso porque uma das expressões é falsa.console.log(!true); // Saída: false // Aqui, `!true` inverte `true` para `false`.Coalescência nula (
??
): Retorna o valor do lado esquerdo se ele não fornull
ouundefined
; caso contrário, retorna o valor do lado direito.let nome = null; let saudacao = nome ?? "Visitante"; console.log(saudacao); // Saída: "Visitante" // Aqui, `nome` é `null`, então `nome ?? "Visitante"` retorna `"Visitante"`.
Os operadores unários operam em um único operando para produzir um novo valor. Exemplos incluem incrementar (++) ou decrementar (--) um valor, e operadores lógicos como a negação (!).
let numero = 5; numero++; console.log(numero); // Saída: 6 // Aqui, `numero` inicialmente vale `5`. `numero++` faz `numero` se tornar `6`.numero--; console.log(numero); // Saída: 5 // Aqui, `numero` inicialmente vale `6`. `numero--` faz `numero` se tornar `5`.
Os operadores bit a bit operam diretamente sobre os bits dos operandos. Eles realizam operações a nível de bit, como AND, OR, XOR, e deslocamentos de bits, permitindo manipulações detalhadas dos dados binários.
console.log(5 & 3); // Saída: 1 (0101 & 0011 = 0001) // Aqui, `5 & 3` compara os bits `0101` e `0011`, resultando em `0001`, que é `1` em decimal.console.log(5 | 3); // Saída: 7 (0101 | 0011 = 0111) // Aqui, `5 | 3` compara os bits `0101` e `0011`, resultando em `0111`, que é `7` em decimal.console.log(5 ^ 3); // Saída: 6 (0101 ^ 0011 = 0110) // Aqui, `5 ^ 3` compara os bits `0101` e `0011`, resultando em `0110`, que é `6` em decimal.console.log(~5); // Saída: -6 (inverte 0101 para 1010) // Aqui, `~5` inverte os bits `0101` para `1010`, que é `-6` em decimal devido à representação de complemento de dois.console.log(5 << 1); // Saída: 10 (0101 << 1 = 1010) // Aqui, `5 << 1` desloca os bits `0101` para a esquerda por um bit, resultando em `1010`, que é `10` em decimal.console.log(5 >> 1); // Saída: 2 (0101 >> 1 = 0010) // Aqui, `5 >> 1` desloca os bits `0101` para a direita por um bit, resultando em `0010`, que é `2` em decimal.Deslocamento para a direita sem sinal (
>>>
): Desloca os bits do operando para a direita, preenchendo com zeros.console.log(5 >>> 1); // Saída: 2 (0101 >>> 1 = 0010) // Aqui, `5 >>> 1` desloca os bits `0101` para a direita por um bit, resultando em `0010`, que é `2` em decimal. A diferença para `>>` é que `>>>` não preserva o sinal.
Os operadores na string são usados principalmente para concatenar strings, unindo duas ou mais strings em uma única string. Isso é útil para construir mensagens dinâmicas, combinar variáveis de texto e manipular dados textuais.
let nome = "Henrique"; let saudacao = "Olá, " + nome + "!"; console.log(saudacao); // Saída: "Olá, Henrique!" // Aqui, `"Olá, " + nome + "!"` concatena as strings `"Olá, "`, `nome` e `"!"`, resultando em `"Olá, Henrique!"`.
Os operadores condicionais são usados para avaliar uma condição e retornar um valor baseado nessa condição. O operador condicional ternário é um exemplo comum.
condição ? valorCasoVerdadeiro : valorCasoFalsolet idade = 18; let podeDirigir = idade >= 18 ? "Sim, pode dirigir." : "Não, não pode dirigir."; console.log(podeDirigir); // Saída: Sim, pode dirigir.Neste exemplo, o operador condicional ? é usado para verificar se a idade é maior ou igual a 18. Se a condição for verdadeira (idade >= 18), o valor "Sim, pode dirigir." é retornado. Caso contrário, "Não, não pode dirigir." é retornado. O resultado é então exibido no console.
OBS: Você pode aprender mais lendo esse artigo: W3S - Operators, W3S - Arithmetic, W3S - Comparison and Logical Operators, W3S - Strings
As funções em JavaScript são blocos de código que realizam uma tarefa específica. Elas podem receber dados de entrada (parâmetros) e retornar um resultado. Funções ajudam a modularizar o código, tornando-o mais organizado e reutilizável.
// Definição de uma função chamada 'dizerOla' que recebe um parâmetro 'nome' function dizerOla(nome) { // A função imprime uma mensagem de saudação no console console.log("Olá, " + nome); } // Chamada da função 'dizerOla' passando o argumento 'Henrique' dizerOla("Henrique"); // Saída: Olá, HenriqueOs parâmetros de função são variáveis listadas como parte da definição da função. Eles permitem que você passe informações para a função.
Os parâmetros padrão permitem inicializar parâmetros com valores padrão se nenhum valor ou
undefined
for passado. Isso é útil para evitar erros quando a função é chamada sem todos os argumentos necessários.// Definição da função 'dizerOla' com um parâmetro padrão 'nome' com valor "Sem Nome" function dizerOla(nome = "Sem Nome") { // A função imprime uma mensagem de saudação no console console.log("Olá, " + name); } // Chamada da função 'dizerOla' sem passar argumentos dizerOla(); // Saída: Olá, Sem Nome // Chamada da função 'dizerOla' passando o argumento 'Henrique' dizerOla("Henrique"); // Saída: Olá, HenriqueOs parâmetros rest permitem representar um número indefinido de argumentos como um array. Isso é útil quando você não sabe de antemão quantos argumentos serão passados para a função.
// Definição da função 'somar' que usa parâmetros rest 'numbers' function somar(...numbers) { // A função usa o método 'reduce' para somar todos os números passados return numbers.reduce((total, numero) => total + numero, 0); } // Chamada da função 'somar' passando múltiplos argumentos console.log(somar(1, 2, 3)); // Saída: 6 console.log(somar(4, 5, 6, 7)); // Saída: 22
Arrow functions são uma sintaxe curta para declarar funções. Elas são mais concisas e têm algumas diferenças de comportamento em comparação com funções tradicionais, especialmente no que diz respeito ao
this
.// Definição de uma arrow function 'add' que recebe dois parâmetros 'a' e 'b' const add = (a, b) => { // A função retorna a soma de 'a' e 'b' return a + b; }; // Chamada da função 'add' passando dois argumentos console.log(add(2, 3)); // Saída: 5 // Outra forma de definir uma arrow function que retorna implicitamente o resultado const mult = (a, b) => a * b; console.log(mult(4, 5)); // Saída: 20
Uma IIFE é uma função que é executada imediatamente após sua definição. Isso é útil para criar um escopo local e evitar poluição do escopo global.
// Definição e invocação imediata de uma IIFE (function() { // O código dentro da IIFE é executado imediatamente console.log("Isso é um IIFE"); })(); // Saída: Isso é um IIFE // Outra forma de definir uma IIFE (() => { console.log("Outro IIFE"); })(); // Saída: Outro IIFE
O objeto
arguments
é uma variável local disponível dentro de todas as funções. Ele contém uma entrada para cada argumento passado para a função, permitindo acesso a todos os argumentos, mesmo que a função não os declare explicitamente.// Definição da função 'multiplicar' que usa o objeto 'arguments' function multiplicar() { // Inicializa a variável 'produto' com o valor 1 let produto = 1; // Itera sobre os argumentos passados usando 'arguments.length' for (let i = 0; i < arguments.length; i++) { // Multiplica 'product' pelo argumento atual produto *= arguments[i]; } // Retorna o resultado final return produto; } // Chamada da função 'multiplicar' passando múltiplos argumentos console.log(multiplicar(2, 3, 4)); // Saída: 24 console.log(multiplicar(1, 2, 3, 4, 5)); // Saída: 120
O escopo refere-se ao contexto atual de execução, no qual valores e expressões são "visíveis" ou podem ser referenciados. A function stack (pilha de funções) é a pilha de execução de chamadas de funções.
Recursão ocorre quando uma função chama a si mesma. É útil para resolver problemas que podem ser divididos em subproblemas menores do mesmo tipo.
// Definição da função 'factorial' que calcula o fatorial de um número 'n' function factorial(n) { // Caso base: se 'n' for 0, retorna 1 if (n === 0) return 1; // Chamada recursiva: multiplica 'n' pelo fatorial de 'n - 1' return n * factorial(n - 1); } // Chamada da função 'factorial' passando o argumento 5 console.log(factorial(5)); // Saída: 120 (5*4*3*2*1) console.log(factorial(0)); // Saída: 1 (caso base)Lexical scoping significa que o escopo de uma variável é determinado pelo local onde ela é definida no código escrito. Funções aninhadas têm acesso às variáveis do escopo externo.
// Definição da função 'dizer' function dizer() { // Declara a variável 'frase' no escopo da função 'dizer' let frase = "Ola, meu nome é Henrique"; // Definição da função 'exibir' aninhada dentro de 'dizer' function exibir() { // A função 'exibir' tem acesso à variável 'frase' devido ao escopo léxico console.log(frase); } // Chamada da função 'exibit' exibir(); } // Chamada da função 'dizer' dizer(); // Saída: Olá, meu nome é HenriqueUm closure é a combinação de uma função e o ambiente léxico no qual ela foi criada. Isso permite que a função acesse >> variáveis de seu escopo externo mesmo após o escopo externo ter sido executado.
// Definição da função 'makeCounter' que retorna uma função function makeCounter() { // Declara a variável 'count' no escopo de 'makeCounter' let count = 0; // Retorna uma função anônima que incrementa e retorna 'count' return function() { count++; return count; }; } // Chama a função 'makeCounter' e armazena o resultado na variável 'counter' const counter = makeCounter(); // Chama a função 'counter' várias vezes console.log(counter()); // Saída: 1 console.log(counter()); // Saída: 2 console.log(counter()); // Saída: 3
JavaScript possui várias funções incorporadas que fornecem funcionalidades comuns. Vamos ver alguns exemplos importantes.
Essas funções analisam uma string e retornam um número inteiro ou de ponto flutuante, respectivamente.
// Usa 'parseInt' para converter uma string em um número inteiro console.log(parseInt("10")); // Saída: 10 // Usa 'parseFloat' para converter uma string em um número de ponto flutuante console.log(parseFloat("10.5")); // Saída: 10.5 // Exemplos com strings que contêm caracteres não numéricos console.log(parseInt("10abc")); // Saída: 10 console.log(parseFloat("10.5abc")); // Saída: 10.5
setTimeout
executa uma função após um período de tempo, esetInterval
executa uma função repetidamente com um intervalo de tempo fixo entre as execuções.// Usa 'setTimeout' para executar uma função após 1 segundo (1000 ms) setTimeout(() => { console.log("Isso é executado após 1 segundo"); }, 1000); // Saída (após 1 segundo): Isso é executado após 1 segundo // Usa 'setInterval' para executar uma função a cada 2 segundos (2000 ms) const intervalId = setInterval(() => { console.log("Isso acontece a cada 2 segundos"); }, 2000); // Saída (a cada 2 segundos): Isso acontece a cada 2 segundos // Para parar o intervalo após 7 segundos setTimeout(() => { clearInterval(intervalId); console.log("Intervalo parado"); }, 7000); // Saída (após 7 segundos): Intervalo paradoOBS: Você pode aprender mais lendo esse artigo: MDN - Functions
O "modo estrito" (strict mode) em JavaScript é uma forma de optar por um comportamento mais restrito no seu código JavaScript, que visa a eliminar alguns erros silenciosos e "más práticas" de programação, além de fornecer mais segurança e melhorar a performance do código. Para ativar o modo estrito, basta adicionar a string
"use strict";
no início de um script ou de uma função.Para ativar o modo estrito, insira
"use strict";
no início de um arquivo JavaScript ou dentro de uma função."use strict"; // Código JavaScript em modo estritofunction exemplo() { "use strict"; // Código JavaScript em modo estrito }
- Declaração de Variáveis:
No modo estrito, você deve declarar todas as variáveis usandovar
,let
ouconst
antes de usá-las. Caso contrário, um erro será gerado."use strict"; x = 3.14; // ReferenceError: x is not defined- Escrita em Propriedades Não-Escrituráveis:
Propriedades que não podem ser modificadas (não-escrituráveis) não podem ser alteradas no modo estrito."use strict"; const obj = {}; Object.defineProperty(obj, 'x', { value: 42, writable: false }); obj.x = 9; // TypeError: Cannot assign to read-only property 'x'- Criação de Propriedades em Objetos Não-Extensíveis:
Novas propriedades não podem ser adicionadas a objetos que foram marcados como não-extensíveis."use strict"; const obj = {}; Object.preventExtensions(obj); obj.newProp = "value"; // TypeError: Cannot add property newProp, object is not extensible- Uso de
eval
:
No modo estrito,eval
não cria ou modifica variáveis no escopo ao seu redor, isolando o código executado dentro deeval
."use strict"; eval("var x = 2;"); console.log(x); // ReferenceError: x is not definedthis
em Funções:
Em funções no modo estrito, o valor dethis
seráundefined
se ele não for explicitamente configurado, enquanto que no modo não estritothis
seria o objeto global (ouwindow
no navegador)."use strict"; function func() { console.log(this); // undefined } func();- Propriedades Duplicadas:
Objetos literais não podem ter propriedades com o mesmo nome."use strict"; const obj = { prop: 1, prop: 2 // SyntaxError: Duplicate data property in object literal not allowed in strict mode };- Nomes de Parâmetros Duplicados:
Funções não podem ter parâmetros com nomes duplicados."use strict"; function soma(a, a, b) { // SyntaxError: Duplicate parameter name not allowed in this context return a + b; }- Literais Octais:
Literais octais (números base 8) são proibidos."use strict"; const num = 010; // SyntaxError: Octal literals are not allowed in strict mode- Proibição de
delete
em Variáveis:
O modo estrito não permite que variáveis, funções ou argumentos sejam excluídos."use strict"; var x = 5; delete x; // SyntaxError: Delete of an unqualified identifier in strict mode.- Palavras Reservadas para Futuro Uso:
Algumas palavras são reservadas para uso futuro e não podem ser usadas como nomes de variáveis no modo estrito."use strict"; const implements = 5; // SyntaxError: Unexpected strict mode reserved word
- Detecção de Erros: O modo estrito ajuda a capturar erros comuns e evita armadilhas do JavaScript, tornando os erros mais visíveis e mais fáceis de corrigir.
- Segurança: Previne algumas práticas que podem levar a vulnerabilidades, como a criação acidental de variáveis globais.
- Otimização: Corrige erros que dificultam a otimização pelos motores JavaScript, ajudando a rodar o código de maneira mais eficiente.
O modo estrito é uma maneira eficaz de melhorar a qualidade do seu código JavaScript, tornando-o mais seguro e menos propenso a erros. Adotar o modo estrito é uma prática recomendada, especialmente em projetos maiores ou ao trabalhar em equipe, para garantir que o código seja mais robusto e sustentável.
OBS: Você pode aprender mais lendo esse artigo: MDN - Strict mode
O
this
é uma palavra-chave importante no JavaScript que muitas vezes confunde os iniciantes. Ele se refere ao contexto de execução, ou seja, ao objeto que está atualmente executando o código. A maneira como o valor dethis
é determinado depende de como uma função é chamada. Vamos explorar detalhadamente os vários usos e comportamentos dothis
.Quando
this
é usado dentro de um método de um objeto, ele se refere ao próprio objeto que contém o método.Abaixo,
saudacao
é um método do objetopessoa
.
Dentro do métodosaudacao
,this
se refere ao objetopessoa
.
Portanto,this.nome
acessa o valor'Rick'
ethis.idade
acessa o valor22
.const pessoa = { nome: 'Rick', idade: 22, saudacao: function() { console.log(`Olá, meu nome é ${this.nome} e eu tenho ${this.idade} anos.`); } }; pessoa.saudacao(); // Olá, meu nome é Rick e eu tenho 22 anos.
detalhes
é um método do objetocarro
.
Dentro do métododetalhes
,this
se refere ao objetocarro
.
Portanto,this.marca
acessa o valor'Toyota'
ethis.modelo
acessa o valor'Corolla'
.const carro = { marca: 'Toyota', modelo: 'Corolla', detalhes: function() { console.log(`Este carro é um ${this.marca} ${this.modelo}.`); } }; carro.detalhes(); // Este carro é um Toyota Corolla.Quando
this
é usado em funções normais, seu valor depende de como a função é chamada. Se a função é chamada sem um contexto de objeto,this
se refere ao objeto global (window
no navegador,global
no Node.js).
mostrarThis
é uma função normal.
ComomostrarThis
é chamada sem um objeto,this
se refere ao objeto global.function mostrarThis() { console.log(this); } mostrarThis(); // No navegador, isso será o objeto Window. No Node.js, será o objeto global.
mostrarValor
é um método do objetoobj
.
Dentro demostrarValor
,interna
é uma função normal.
Quandointerna
é chamada,this
se refere ao objeto global, ondevalor
não está definido, resultando emundefined
.const obj = { valor: 42, mostrarValor: function() { function interna() { console.log(this.valor); } interna(); } }; obj.mostrarValor(); // undefinedQuando
this
é usado sozinho fora de qualquer função, no contexto global, ele se refere ao objeto global.Fora de qualquer função,
this
se refere ao objeto global.console.log(this); // No navegador, isso será o objeto Window. No Node.js, será o objeto global.A função
globalThis
retornathis
, que se refere ao objeto global quando a função é chamada no contexto global.function globalThis() { return this; } console.log(globalThis() === window); // true no navegador console.log(globalThis() === global); // true no Node.jsEm manipuladores de eventos,
this
se refere ao elemento HTML que recebeu o evento.O
this
dentro do manipulador de eventos se refere ao elementobutton
que foi clicado.<button id="meuBotao">Clique aqui</button> <script> document.getElementById('meuBotao').addEventListener('click', function() { console.log(this); // <button id="meuBotao">Clique aqui</button> }); </script>O
this
dentro do manipulador de eventos se refere ao elementodiv
que foi clicado.
A cor de fundo dodiv
muda para amarelo quando clicado.<div id="meuDiv"> Clique aqui </div> <script> document.getElementById('meuDiv').addEventListener('click', function() { this.style.backgroundColor = 'yellow'; }); </script>Arrow functions (funções de seta) não têm seu próprio
this
. Em vez disso, herdam othis
do contexto onde foram definidas.
arrowFunc
é uma arrow function definida dentro do métodometodo
.
arrowFunc
herda othis
do métodometodo
, que se refere ao objetoobjeto
.const objeto = { valor: 10, metodo: function() { const arrowFunc = () => { console.log(this.valor); }; arrowFunc(); // 10 } }; objeto.metodo();A arrow function dentro do
setTimeout
herda othis
do contexto da funçãoComum
.
Quando a funçãoComum
é instanciada,this
se refere à nova instância criada.function Comum() { this.valor = 42; setTimeout(() => { console.log(this.valor); // 42 }, 1000); } new Comum();Function borrowing é a prática de usar métodos de um objeto em outro objeto.
pessoa1.saudacao
é emprestada parapessoa2
usandocall
.
Othis
dentro desaudacao
se refere apessoa2
quando chamado comcall
.const pessoa1 = { nome: 'Henrique', saudacao: function() { console.log(`Olá, eu sou ${this.nome}`); } }; const pessoa2 = { nome: 'Rick' }; pessoa1.saudacao.call(pessoa2); // Olá, eu sou Rick
carro1.detalhes
é emprestada paracarro2
usandoapply
.
Othis
dentro dedetalhes
se refere acarro2
quando chamado comapply
.const carro1 = { marca: 'Honda', modelo: 'Civic', detalhes: function() { console.log(`Carro: ${this.marca} ${this.modelo}`); } }; const carro2 = { marca: 'Ford', modelo: 'Mustang' }; carro1.detalhes.apply(carro2); // Carro: Ford MustangO método
call
chama uma função com um determinadothis
e argumentos passados individualmente.
call
é usado para chamarapresentar
comthis
definido comonull
e argumentos'Rick'
e22
.function apresentar(nome, idade) { console.log(`Nome: ${nome}, Idade: ${idade}`); } apresentar.call(null, 'Alice', 25); // Nome: Rick, Idade: 22
call
é usado para chamardizerNome
comthis
definido comopessoa
.const pessoa = { nome: 'Henrique' }; function dizerNome() { console.log(this.nome); } dizerNome.call(pessoa); // HenriqueO método
apply
é semelhante aocall
, mas os argumentos são passados como um array.
apply
é usado para chamarcalcular
comthis
definido comonull
e argumentos[1, 2, 3]
.function calcular(a, b, c) { return a + b + c; } console.log(calcular.apply(null, [1, 2, 3])); // 6
apply
é usado para chamardizerNomeEIdade
comthis
definido comopessoa
e argumento22
.const pessoa = { nome: 'Rick Lustri' }; function dizerNomeEIdade(idade) { console.log(`Nome: ${this.nome}, Idade: ${idade}`); } dizerNomeEIdade.apply(pessoa, [22]); // Nome: Rick Lustri, Idade: 22O método
bind
cria uma nova função que, quando chamada, tem seuthis
definido com um valor específico.const pessoa = { nome: 'Henrique' }; function saudacao() { console.log(`Olá, ${this.nome}`); } const saudacaoHenrique = saudacao.bind(pessoa); saudacaoHenrique(); // Olá, Henrique
bind
é usado para criar uma nova funçãosaudacaoHenrique
comthis
definido comopessoa
.
bind
é usado para criar uma nova funçãodetalhesBMW
comthis
definido comocarro
.const carro = { marca: 'BMW', modelo: 'X5' }; function detalhesCarro() { console.log(`Marca: ${this.marca}, Modelo: ${this.modelo}`); } const detalhesBMW = detalhesCarro.bind(carro); detalhesBMW(); // Marca: BMW, Modelo: X5OBS: Você pode aprender mais lendo esse artigo: MDN - this
setTimeout
é uma função JavaScript que permite executar um pedaço de código ou uma função após um determinado período de tempo. Esse tempo é especificado em milissegundos. A funçãosetTimeout
é útil para atrasar a execução de uma ação ou tarefa.setTimeout(function, delay, ...arguments);
function
: A função que será executada após o tempo especificado.
delay
: O tempo em milissegundos após o qual a função será executada.
arguments
: Argumentos adicionais que podem ser passados para a função.// Função a ser executada function falarOla(nome) { console.log(`Olá, ${nome}!`); } // Definindo o tempo de atraso em 2000 milissegundos (2 segundos) const segundos = 2000; // Chamando setTimeout para executar falarOla após 2 segundos setTimeout(falarOla, segundos, "Henrique");
- Definimos a função
falarOla
que aceita um parâmetronome
.- Usamos
setTimeout
para chamarfalarOla
após 2 segundos (2000 milissegundos).- O terceiro argumento
"Henrique"
é passado para a funçãofalarOla
quando ela for executada.- Após 2 segundos, a mensagem
"Olá, Henrique!"
será exibida no console.Você pode cancelar um
setTimeout
usando a funçãoclearTimeout
. Para isso, você precisa armazenar o ID dosetTimeout
em uma variável e então chamarclearTimeout
com esse ID.// Função a ser executada function falarOla(nome) { console.log(`Olá, ${nome}!`); } // Definindo o tempo de atraso em 5000 milissegundos (5 segundos) const segundos = 5000; // Chamando setTimeout para executar falarOla após 5 segundos const timeoutId = setTimeout(falarOla, segundos, "Rick"); // Cancelando o setTimeout antes que ele seja executado clearTimeout(timeoutId);
- Definimos a função
falarOla
que aceita um parâmetronome
.- Usamos
setTimeout
para chamarfalarOla
após 5 segundos (5000 milissegundos).- O
ID
retornado porsetTimeout
é armazenado na variáveltimeoutId
.- Chamamos
clearTimeout
comtimeoutId
para cancelar a execução defalarOla
.- Como resultado, a função
falarOla
não será executada e a mensagem"Olá, Rick!"
não será exibida.
setInterval
é uma função JavaScript que permite executar repetidamente um pedaço de código ou uma função em intervalos de tempo regulares. O intervalo é especificado em milissegundos. A funçãosetInterval
é útil para executar tarefas periódicas.setInterval(function, delay, ...arguments);
function
: A função que será executada repetidamente.
delay
: O intervalo de tempo em milissegundos entre cada execução da função.
arguments
: Argumentos adicionais que podem ser passados para a função.// Função a ser executada function mostrarHoraAtual() { const time = new Date(); console.log(`Hora atual: ${time.toLocaleTimeString()}`); } // Definindo o intervalo em 1000 milissegundos (1 segundo) const intervalo = 1000; // Chamando setInterval para executar smostrarHoraAtual a cada 1 segundo const intervalId = setInterval(mostrarHoraAtual, intervalo);
- Definimos a função
mostrarHoraAtual
que exibe a hora atual no console.- Usamos
setInterval
para chamarmostrarHoraAtual
a cada 1 segundo (1000 milissegundos).- O
ID
retornado porsetInterval
é armazenado na variávelintervalId
.- A cada 1 segundo, a hora atual será exibida no console.
Você pode cancelar um
setInterval
usando a funçãoclearInterval
. Para isso, você precisa armazenar o ID dosetInterval
em uma variável e então chamarclearInterval
com esse ID.// Função a ser executada function mostrarHoraAtual() { const time = new Date(); console.log(`Hora atual: ${time.toLocaleTimeString()}`); } // Definindo o intervalo em 1000 milissegundos (1 segundo) const intervalo = 1000; // Chamando setInterval para executar smostrarHoraAtual a cada 1 segundo const intervalId = setInterval(mostrarHoraAtual, intervalo); // Cancelando o setInterval após 5 segundos setTimeout(() => clearInterval(intervalId), 5000);
- Definimos a função
mostrarHoraAtual
que exibe a hora atual no console.- Usamos
setInterval
para chamarmostrarHoraAtual
a cada 1 segundo (1000 milissegundos).- O
ID
retornado porsetInterval
é armazenado na variávelintervalId
.- Usamos
setTimeout
para cancelar osetInterval
após 5 segundos (5000 milissegundos).- Como resultado, a função
mostrarHoraAtual
será executada 5 vezes antes de ser cancelada.
O Event Loop é um mecanismo fundamental no JavaScript que permite a execução de código assíncrono. Ele monitora a pilha de chamadas (call stack) e a fila de mensagens (message queue) para garantir que o código assíncrono, como callbacks, seja executado quando a pilha de chamadas estiver vazia.
- Call Stack (Pilha de Chamadas): Onde o código é executado. Cada vez que uma função é chamada, ela é adicionada à pilha. Quando a função termina sua execução, ela é removida da pilha.
- Message Queue (Fila de Mensagens): Onde as mensagens (ou callbacks) aguardam para serem executadas. Cada mensagem é uma função que precisa ser processada.
- Event Loop (Loop de Eventos): O Event Loop verifica continuamente se a pilha de chamadas está vazia. Se estiver, ele pega a primeira mensagem da fila de mensagens e a coloca na pilha de chamadas para ser executada.
Vamos ver como o Event Loop funciona com um exemplo prático.
console.log('Start'); // 1. Adiciona 'Start' à pilha de chamadas e executa setTimeout(() => { console.log('Callback 1'); // 4. Adiciona 'Callback 1' à fila de mensagens após 0 ms }, 0); console.log('Meio'); // 2. Adiciona 'Meio' à pilha de chamadas e executa setTimeout(() => { console.log('Callback 2'); // 5. Adiciona 'Callback 2' à fila de mensagens após 1000 ms }, 1000); console.log('End'); // 3. Adiciona 'End' à pilha de chamadas e executa // Ordem de execução esperada: // 'Start' // 'Meio' // 'End' // 'Callback 1' // 'Callback 2'
'Start'
é adicionado à pilha de chamadas e é imediatamente executado, exibindo'Start'
no console.setTimeout
é chamado com um atraso de 0 ms. A função decallback
é colocada na fila de mensagens para ser executada depois que a pilha de chamadas estiver vazia.'Meio'
é adicionado à pilha de chamadas e é imediatamente executado, exibindo'Meio'
no console.- Outro
setTimeout
é chamado com um atraso de 1000 ms. A função decallback
é colocada na fila de mensagens para ser executada após 1 segundo.'End'
é adicionado à pilha de chamadas e é imediatamente executado, exibindo'End'
no console.- Após a pilha de chamadas estar vazia, o Event Loop verifica a fila de mensagens e encontra
'Callback 1'
, que é então adicionado à pilha de chamadas e executado, exibindo'Callback 1'
no console.- Após 1 segundo, o Event Loop verifica novamente a fila de mensagens e encontra
'Callback 2'
, que é então adicionado à pilha de chamadas e executado, exibindo'Callback 2'
no console.
Call Stack (Pilha de Chamadas):
console.log('Start')
->console.log('Meio')
->console.log('End')
->console.log('Callback 1')
->console.log('Callback 2')
Message Queue (Fila de Mensagens):
- Após
setTimeout(() => { console.log('Callback 1'); }, 0)
, a função de callback é colocada na fila de mensagens.- Após
setTimeout(() => { console.log('Callback 2'); }, 1000)
, a função de callback é colocada na fila de mensagens após 1 segundo.Event Loop (Loop de Eventos):
- Verifica continuamente se a pilha de chamadas está vazia.
- Quando a pilha de chamadas está vazia, pega a próxima mensagem na fila de mensagens e a coloca na pilha de chamadas para ser executada.
O Event Loop permite que o JavaScript seja não bloqueante e assíncrono, o que significa que ele pode lidar com múltiplas operações de I/O (entrada/saída) sem bloquear a execução do código. Isso é crucial para a construção de aplicações web eficientes e responsivas.
Callbacks são funções passadas como argumentos para outras funções e são chamadas (ou "invocadas") dentro da função que as recebe. No JavaScript, os callbacks são frequentemente usados para operações assíncronas, como leitura de arquivos, consultas a bancos de dados, ou requisições HTTP.
function doSomething(callback) { // Código assíncrono callback(); }// Função que simula uma operação assíncrona usando setTimeout function buscarDados(callback) { console.log('Buscando dados...'); setTimeout(() => { const dados = { nome: 'Henrique', idade: 22 }; callback(dados); }, 2000); // Simula um atraso de 2 segundos } // Função de callback que processa os dados function processarDados(dados) { console.log('Informação recebida:', dados); } // Chamando a função assíncrona com o callback buscarDados(processarDados);
- Definimos a função
buscarDados
que aceita umcallback
como parâmetro.- Dentro de
buscarDados
, usamossetTimeout
para simular uma operação assíncrona com um atraso de 2 segundos.- Após o atraso, a função de
callback
é chamada com os dados simulados.- Definimos a função
processarDados
que aceita os dados como parâmetro e os exibe no console.- Chamamos
buscarDados
e passamosprocessarDados
como o callback.- Após 2 segundos, a mensagem
'Informação recebida: { nome: "Henrique", idade: 22 }'
é exibida no console.Callback Hell é uma situação que ocorre quando vários callbacks aninhados são usados, resultando em código difícil de ler e manter. Isso geralmente acontece em operações assíncronas que dependem de várias etapas ou chamadas de função sequenciais.
// Simulação de uma série de operações assíncronas dependentes function step1(value, callback) { console.log('Step 1:', value); setTimeout(() => callback(value + 1), 1000); // Atraso de 1 segundo } function step2(value, callback) { console.log('Step 2:', value); setTimeout(() => callback(value + 1), 1000); // Atraso de 1 segundo } function step3(value, callback) { console.log('Step 3:', value); setTimeout(() => callback(value + 1), 1000); // Atraso de 1 segundo } // Executando as funções em sequência com callbacks aninhados step1(0, (result1) => { step2(result1, (result2) => { step3(result2, (result3) => { console.log('Final result:', result3); }); }); });
- Definimos três funções assíncronas:
step1
,step2
estep3
, cada uma com um atraso de 1 segundo.- Cada função exibe o valor atual e chama o
callback
com o valor incrementado em 1.- Chamamos
step1
com o valor inicial 0 e umcallback
que chamastep2
.- O
callback
destep2
chamastep3
com o valor resultante destep2
.- O
callback
destep3
exibe o resultado final no console.- O código é difícil de ler devido ao aninhamento de callbacks.
Existem várias maneiras de evitar o Callback Hell:
- Modularizar Funções: Separar funções em partes menores e reutilizáveis.
- Promises: Utilizar Promises para lidar com operações assíncronas de forma mais limpa.
- Async/Await: Usar
async
eawait
para escrever código assíncrono que se pareça com código síncrono.// Convertendo funções para retornar Promises function step1(value) { return new Promise((resolve) => { console.log('Step 1:', value); setTimeout(() => resolve(value + 1), 1000); // Atraso de 1 segundo }); } function step2(value) { return new Promise((resolve) => { console.log('Step 2:', value); setTimeout(() => resolve(value + 1), 1000); // Atraso de 1 segundo }); } function step3(value) { return new Promise((resolve) => { console.log('Step 3:', value); setTimeout(() => resolve(value + 1), 1000); // Atraso de 1 segundo }); } // Encadeando Promises step1(0) .then(step2) .then(step3) .then((result) => { console.log('Final result:', result); });
- Convertendo
step1
,step2
estep3
para retornar Promises.- Cada função retorna uma Promise que resolve com o valor incrementado em 1 após 1 segundo.
- Usamos
.then()
para encadear as Promises, tornando o código mais legível.- O resultado final é exibido no console após todas as operações assíncronas serem concluídas.
// Funções assíncronas retornando Promises function step1(value) { return new Promise((resolve) => { console.log('Step 1:', value); setTimeout(() => resolve(value + 1), 1000); // Atraso de 1 segundo }); } function step2(value) { return new Promise((resolve) => { console.log('Step 2:', value); setTimeout(() => resolve(value + 1), 1000); // Atraso de 1 segundo }); } function step3(value) { return new Promise((resolve) => { console.log('Step 3:', value); setTimeout(() => resolve(value + 1), 1000); // Atraso de 1 segundo }); } // Função assíncrona usando async/await async function executeSteps() { const result1 = await step1(0); const result2 = await step2(result1); const result3 = await step3(result2); console.log('Final result:', result3); } // Chamando a função assíncrona executeSteps();
- Usamos
async/await
para escrever código assíncrono de maneira mais legível.- A função
executeSteps
é declarada como assíncrona com a palavra-chaveasync
.- Usamos
await
para esperar a resolução de cada Promise retornada porstep1
,step2
estep3
.- O resultado final é exibido no console após todas as operações assíncronas serem concluídas.
Promises são objetos que representam a eventual conclusão (ou falha) de uma operação assíncrona e seu valor resultante. Uma Promise pode estar em um dos três estados:
- Pending (Pendente): Estado inicial, que ainda não foi resolvido ou rejeitado.
- Fulfilled (Resolvida): A operação assíncrona foi concluída com sucesso e a Promise tem um valor.
- Rejected (Rejeitada): A operação assíncrona falhou e a Promise tem um motivo pelo qual foi rejeitada.
let promise = new Promise(function(resolve, reject) { // Código assíncrono if (/* operação bem-sucedida */) { resolve(value); } else { reject(error); } });Vamos ver como criar e usar Promises com um exemplo.
// Criando uma função que retorna uma Promise function buscarDados() { return new Promise((resolve, reject) => { console.log('Buscando dados...'); setTimeout(() => { const dados = { nome: 'Rick', idade: 22 }; if (dados) { resolve(dados); // Resolve a Promise com os dados } else { reject('Error: Dados não encontrado'); // Rejeita a Promise com um erro } }, 2000); // Simula um atraso de 2 segundos }); } // Usando a Promise buscarDados() .then((dados) => { console.log('Dados recebidos', dados); }) .catch((error) => { console.error(error); });
- Criamos a função
buscarDados
que retorna uma Promise.- Dentro da Promise, usamos
setTimeout
para simular uma operação assíncrona com um atraso de 2 segundos.- Se os dados são encontrados, chamamos
resolve(dados)
para resolver a Promise.- Se ocorrer um erro, chamamos
reject('Error: Dados não encontrado)
para rejeitar a Promise.- Usamos
.then()
para manipular a resolução da Promise e exibir os dados recebidos no console.- Usamos
.catch()
para manipular a rejeição da Promise e exibir o erro no console.
async
eawait
são palavras-chave do JavaScript que simplificam o trabalho com código assíncrono, permitindo que você escreva código assíncrono que parece síncrono.
- async: Define uma função assíncrona que retorna uma Promise.
- await: Pausa a execução da função assíncrona até que a Promise seja resolvida ou rejeitada.
async function myFunction() { let result = await somePromise; }Vamos ver como usar
async
eawait
para simplificar o exemplo anterior.// Função assíncrona que retorna uma Promise function buscarDados() { return new Promise((resolve, reject) => { console.log('Buscando dados...'); setTimeout(() => { const dados = { nome: 'RickLustri', idade: 22 }; if (dados) { resolve(dados); // Resolve a Promise com os dados } else { reject('Error: Dados não encontrado'); // Rejeita a Promise com um erro } }, 2000); // Simula um atraso de 2 segundos }); } // Função assíncrona usando async/await async function displayData() { try { const data = await buscarDados(); // Espera a resolução da Promise console.log('Data received:', data); } catch (error) { console.error(error); // Manipula a rejeição da Promise } } // Chamando a função assíncrona displayData();
- A função
buscarDados
permanece a mesma, retornando uma Promise.- Definimos a função
displayData
como assíncrona usando a palavra-chaveasync
.- Dentro de
displayData
, usamosawait
para esperar a resolução da Promise retornada porbuscarDados
.- Se a Promise for resolvida, os dados são exibidos no console.
- Usamos
try...catch
para manipular possíveis erros (rejeição da Promise).- Chamamos
displayData
para executar o código assíncrono.buscarDados() .then((dados) => { console.log('Dados encontrados:', dados); }) .catch((error) => { console.error(error); });async function displayData() { try { const dados = await buscarDados(); console.log('Dados encotrados:', dados); } catch (error) { console.error(error); } } displayData();
- Leitura mais limpa e intuitiva: Código assíncrono parece síncrono, facilitando a leitura e manutenção.
- Menos aninhamento: Evita o problema de "Promise chaining" (encadeamento de Promises) excessivo, tornando o código mais linear.
OBS: Você pode aprender mais lendo esse artigo: MDN - Promise, MDN - async/await, MDN - Callback e MDN - Event loop
XMLHttpRequest
é um objeto em JavaScript que permite fazer requisições HTTP para interagir com servidores. Ele pode ser usado para buscar dados de um servidor e atualizar partes de uma página web sem recarregar a página inteira. Isso é uma parte fundamental do AJAX (Asynchronous JavaScript and XML).Para usar
XMLHttpRequest
, você primeiro precisa criar uma instância desse objeto:let xhr = new XMLHttpRequest();Os métodos mais importantes do
XMLHttpRequest
são:
open(method, url, async)
: Inicializa a requisição.method
é o método HTTP (GET, POST, etc.),url
é o endpoint, easync
define se a requisição será assíncrona (true) ou síncrona (false).send(body)
: Envia a requisição.body
é opcional e usado principalmente em requisições POST.setRequestHeader(header, value)
: Define cabeçalhos HTTP adicionais.
onreadystatechange
: Evento disparado sempre que oreadyState
doXMLHttpRequest
muda.onload
: Evento disparado quando a requisição é completada com sucesso.onerror
: Evento disparado quando ocorre um erro na requisição.
readyState
: Retorna o estado atual da requisição. Os estados possíveis são:
0
: Não inicializado1
: Conexão estabelecida2
: Requisição recebida3
: Processando requisição4
: Requisição concluída e a resposta está prontastatus
: Retorna o código de status HTTP da resposta (ex: 200 para sucesso).responseText
: Retorna a resposta da requisição como uma string de texto.responseXML
: Retorna a resposta da requisição como um documento XML.Vamos fazer uma requisição GET para um API pública que retorna dados de usuários:
// Cria uma nova instância do XMLHttpRequest let xhr = new XMLHttpRequest(); // Define o método e o URL para a requisição xhr.open('GET', 'https://jsonplaceholder.typicode.com/users', true); // Define o que fazer quando a resposta for carregada xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { // Sucesso: parseia a resposta JSON let users = JSON.parse(xhr.responseText); console.log(users); // Exibe os dados dos usuários no console } else { // Se ocorreu um erro console.error('Erro na requisição:', xhr.statusText); } }; // Define o que fazer se ocorrer um erro na requisição xhr.onerror = function() { console.error('Erro na conexão'); }; // Envia a requisição xhr.send();
Criando o objeto:
let xhr = new XMLHttpRequest();
- Cria uma nova instância de
XMLHttpRequest
.Inicializando a requisição:
xhr.open('GET', 'https://jsonplaceholder.typicode.com/users', true);
- Configura a requisição para usar o método GET e acessar a URL especificada. A requisição será assíncrona (true).
Configurando o evento onload:
xhr.onload
define uma função que será chamada quando a resposta for carregada com sucesso.if (xhr.status >= 200 && xhr.status < 300)
verifica se o status da resposta está no intervalo de sucesso (200–299).let users = JSON.parse(xhr.responseText);
converte a resposta JSON em um objeto JavaScript.console.log(users);
exibe os dados no console.Configurando o evento onerror:
xhr.onerror
define uma função que será chamada se ocorrer um erro na requisição.Enviando a requisição:
xhr.send();
- Envia a requisição ao servidor.
Agora, vamos fazer uma requisição POST para enviar dados para o servidor:
// Cria uma nova instância do XMLHttpRequest let xhr = new XMLHttpRequest(); // Define o método e o URL para a requisição xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts', true); // Define o cabeçalho da requisição como JSON xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); // Define o que fazer quando a resposta for carregada xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { // Sucesso: parseia a resposta JSON let response = JSON.parse(xhr.responseText); console.log(response); // Exibe a resposta no console } else { // Se ocorreu um erro console.error('Erro na requisição:', xhr.statusText); } }; // Define o que fazer se ocorrer um erro na requisição xhr.onerror = function() { console.error('Erro na conexão'); }; // Define os dados que serão enviados let data = JSON.stringify({ title: 'foo', body: 'bar', userId: 1 }); // Envia a requisição com os dados xhr.send(data);
Criando o objeto:
let xhr = new XMLHttpRequest();
- Cria uma nova instância de
XMLHttpRequest
.Inicializando a requisição:
xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts', true);
- Configura a requisição para usar o método POST e acessar a URL especificada. A requisição será assíncrona (true).
Definindo o cabeçalho da requisição:
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
- Define que o corpo da requisição será JSON.
Configurando o evento onload:
xhr.onload
define uma função que será chamada quando a resposta for carregada com sucesso.if (xhr.status >= 200 && xhr.status < 300)
verifica se o status da resposta está no intervalo de sucesso (200–299).let response = JSON.parse(xhr.responseText);
converte a resposta JSON em um objeto JavaScript.console.log(response);
exibe os dados no console.Configurando o evento onerror:
xhr.onerror
define uma função que será chamada se ocorrer um erro na requisição.Definindo os dados:
let data = JSON.stringify({ title: 'foo', body: 'bar', userId: 1 });
- Converte os dados em uma string JSON para serem enviados.
Enviando a requisição com os dados:
xhr.send(data);
- Envia a requisição ao servidor com os dados JSON.
O
XMLHttpRequest
é uma ferramenta poderosa para fazer requisições HTTP assíncronas e síncronas em JavaScript. No entanto, ele está sendo cada vez mais substituído pelofetch
, que oferece uma API mais moderna e simplificada para realizar operações semelhantes. Contudo, ainda é importante compreenderXMLHttpRequest
, pois é amplamente utilizado em muitas aplicações web existentes.
Fetch API é uma interface moderna que permite fazer requisições HTTP de forma mais simples e intuitiva do que o
XMLHttpRequest
. A Fetch API usa Promises, que facilitam o manuseio de requisições assíncronas.A Fetch API é utilizada através da função global
fetch()
, que retorna uma Promise. Esta Promise resolve para o objetoResponse
, representando a resposta à requisição.A sintaxe básica do
fetch
é:fetch(url, options) .then(response => { // Processa a resposta aqui }) .catch(error => { // Lida com o erro aqui });Vamos fazer uma requisição GET para um API pública que retorna dados de usuários:
// URL da API const url = 'https://jsonplaceholder.typicode.com/users'; // Fazendo a requisição GET fetch(url) .then(response => { // Verifica se a resposta está ok (status 200-299) if (!response.ok) { throw new Error('Network response was not ok ' + response.statusText); } // Converte a resposta para JSON return response.json(); }) .then(data => { // Processa os dados console.log(data); // Exibe os dados no console }) .catch(error => { // Lida com os erros console.error('There has been a problem with your fetch operation:', error); });
Definindo a URL:
const url = 'https://jsonplaceholder.typicode.com/users';
- Define a URL do endpoint da API.
Fazendo a requisição GET:
fetch(url)
- Faz a requisição para a URL especificada.
Processando a resposta:
response.ok
verifica se a resposta foi bem-sucedida.response.json()
converte a resposta em JSON.Manipulando os dados:
console.log(data);
- Exibe os dados da resposta no console.
Lidando com erros:
catch(error => { ... });
- Captura e exibe quaisquer erros que ocorram durante a requisição.
Vamos fazer uma requisição POST para enviar dados para o servidor:
// URL da API const url = 'https://jsonplaceholder.typicode.com/posts'; // Dados a serem enviados const data = { title: 'foo', body: 'bar', userId: 1 }; // Fazendo a requisição POST fetch(url, { method: 'POST', // Método HTTP headers: { 'Content-Type': 'application/json; charset=UTF-8' // Cabeçalhos HTTP }, body: JSON.stringify(data) // Dados enviados no corpo da requisição }) .then(response => { // Verifica se a resposta está ok (status 200-299) if (!response.ok) { throw new Error('Network response was not ok ' + response.statusText); } // Converte a resposta para JSON return response.json(); }) .then(data => { // Processa os dados console.log(data); // Exibe a resposta no console }) .catch(error => { // Lida com os erros console.error('There has been a problem with your fetch operation:', error); });
Definindo a URL:
const url = 'https://jsonplaceholder.typicode.com/posts';
- Define a URL do endpoint da API.
Definindo os dados:
const data = { ... };
- Define os dados a serem enviados no corpo da requisição.
Fazendo a requisição POST:
fetch(url, { method: 'POST', headers: { ... }, body: JSON.stringify(data) })
- Configura a requisição para usar o método POST, define os cabeçalhos e converte os dados para JSON.
Processando a resposta:
response.ok
verifica se a resposta foi bem-sucedida.response.json()
converte a resposta em JSON.Manipulando os dados:
console.log(data);
- Exibe os dados da resposta no console.
Lidando com erros:
catch(error => { ... });
- Captura e exibe quaisquer erros que ocorram durante a requisição.
A Fetch simplifica a manipulação de respostas, especialmente no que diz respeito ao tratamento de diferentes tipos de dados (como JSON, texto, e até mesmo blobs).
Algumas APIs exigem autenticação, que pode ser facilmente adicionada usando cabeçalhos.
// URL da API const url = 'https://api.example.com/protected'; // Fazendo a requisição GET com autenticação fetch(url, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_ACCESS_TOKEN' } }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok ' + response.statusText); } return response.json(); }) .then(data => { console.log(data); }) .catch(error => { console.error('There has been a problem with your fetch operation:', error); });
Definindo a URL:
const url = 'https://api.example.com/protected';
- Define a URL do endpoint da API.
Fazendo a requisição GET com autenticação:
fetch(url, { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_ACCESS_TOKEN' } })
- Configura a requisição para usar o método GET e inclui o cabeçalho de autenticação.
Processando a resposta:
response.ok
verifica se a resposta foi bem-sucedida.response.json()
converte a resposta em JSON.Manipulando os dados:
console.log(data);
- Exibe os dados da resposta no console.
Lidando com erros:
catch(error => { ... });
- Captura e exibe quaisquer erros que ocorram durante a requisição.
A Fetch API é uma maneira moderna e simplificada de fazer requisições HTTP em JavaScript. Ela oferece uma interface mais fácil de usar do que o
XMLHttpRequest
e permite um manuseio mais intuitivo de Promises, facilitando o trabalho com operações assíncronas. Através de exemplos comentados e detalhados, é possível entender como realizar diferentes tipos de requisições e como lidar com as respostas e possíveis erros.OBS: Você pode aprender mais lendo esse artigo: MDN - XMLHttpRequest e MDN - Fetch API
Classes em JavaScript são uma forma moderna de criar objetos e implementar conceitos de Programação Orientada a Objetos (POO). Introduzidas no ECMAScript 6 (ES6), as classes fornecem uma sintaxe mais simples e familiar para desenvolvedores que vêm de outras linguagens orientadas a objetos, como Java ou C#. Embora JavaScript continue sendo uma linguagem baseada em protótipos, as classes oferecem uma maneira mais estruturada e legível de definir e herdar objetos.
Vamos definir uma classe simples chamada
Pessoa
. Uma classe em JavaScript é definida usando a palavra-chaveclass
, seguida pelo nome da classe.// Definindo a classe Pessoa class Pessoa { // O método constructor é um método especial para criar e inicializar um objeto criado a partir de uma classe. constructor(nome, idade) { this.nome = nome; // Atribui o valor do argumento 'nome' à propriedade 'nome' do objeto this.idade = idade; // Atribui o valor do argumento 'idade' à propriedade 'idade' do objeto } // Método de instância - estará disponível em todas as instâncias da classe apresentar() { console.log(`Olá, meu nome é ${this.nome} e eu tenho ${this.idade} anos.`); } // Método estático - disponível apenas na classe, não nas instâncias static especie() { console.log('Humana'); } } // Criando uma instância da classe Pessoa const pessoa1 = new Pessoa('Henrique', 22); // Chamando o método de instância 'apresentar' pessoa1.apresentar(); // Olá, meu nome é Henrique e eu tenho 22 anos. // Chamando o método estático 'especie' diretamente na classe Pessoa.especie(); // Humana
- Definição da Classe:
class Pessoa
define uma nova classe chamadaPessoa
.- Construtor:
constructor(nome, idade)
é um método especial que inicializa novos objetos da classe.this.nome
ethis.idade
são propriedades definidas com base nos argumentos passados ao construtor.- Método de Instância:
apresentar()
é um método que pode ser chamado em qualquer instância da classePessoa
.- Método Estático:
static especie()
é um método que pode ser chamado diretamente na classePessoa
, não em suas instâncias.- Instância da Classe:
const pessoa1 = new Pessoa('Henrique', 22)
cria um novo objeto da classePessoa
.- Chamando Métodos:
pessoa1.apresentar()
chama o métodoapresentar
na instânciapessoa1
.Pessoa.especie()
chama o método estáticoespecie
na classePessoa
.A herança permite que uma classe herde propriedades e métodos de outra classe, facilitando a reutilização de código e a criação de uma hierarquia de classes.
// Definindo uma classe chamada 'Funcionario' que herda da classe 'Pessoa' class Funcionario extends Pessoa { constructor(nome, idade, meuTrabalho) { super(nome, idade); // Chama o construtor da classe pai (Pessoa) this.meuTrabalho = meuTrabalho; // Atribui o valor do argumento 'meuTrabalho' à propriedade 'meuTrabalho' do objeto } // Sobrescrevendo o método 'apresentar' da classe pai apresentar() { console.log(`Olá, meu nome é ${this.nome}, eu sou um(a) ${this.meuTrabalho} e eu tenho ${this.idade} anos.`); } // Método específico da classe 'Funcionario' trabalho() { console.log(`${this.nome} está trabalhando como ${this.meuTrabalho}.`); } } // Criando uma instância da classe 'Funcionario' const funcionario1 = new Funcionario('Rick', 22, 'Desenvolvedor'); // Chamando o método de instância 'apresentar' funcionario1.apresentar(); // Olá, meu nome é Rick, eu sou um(a) Desenvolvedor e eu tenho 22 anos. // Chamando o método específico 'trabalho' funcionario1.trabalho(); // Rick está trabalhando como Desenvolvedor.
- Definição da Classe Derivada:
class Funcionario extends Pessoa
define uma nova classeFuncionario
que herda da classePessoa
.- Construtor:
constructor(nome, idade, meuTrabalho)
inicializa a instância da classeFuncionario
, chamandosuper(nome, idade)
para herdar as propriedadesnome
eidade
da classePessoa
.- Método Sobrescrito:
apresentar()
na classeFuncionario
sobrescreve o métodoapresentar
da classePessoa
.- Novo Método:
trabalho()
é um método específico da classeFuncionario
.- Instância da Classe:
const funcionario1 = new Funcionario('Rick', 22, 'Desenvolvedor')
cria um novo objeto da classeFuncionario
.- Chamando Métodos:
funcionario1.apresentar()
chama o métodoapresentar
sobrescrito na instânciafuncionario1
.funcionario1.trabalho()
chama o métodotrabalho
na instânciafuncionario1
.O encapsulamento é uma prática de programação que esconde os detalhes internos de um objeto e expõe apenas o que é necessário. Em JavaScript, usamos
#
para declarar campos privados em classes.// Definindo uma classe 'ContaBancaria' com campos privados class ContaBancaria { // Campos privados são prefixados com # #saldo; constructor(dono, saldo) { this.dono = dono; // Atribui o valor do argumento 'dono' à propriedade 'dono' do objeto this.#saldo = saldo; // Inicializa o campo privado '#saldo' com o valor do argumento 'saldo' } // Método público para depositar dinheiro depositar(quantidade) { this.#saldo += quantidade; console.log(`${this.dono} depositou ${quantidade}. Saldo atual: ${this.#saldo}`); } // Método público para sacar dinheiro retirar(quantidade) { if (quantidade > this.#saldo) { console.log('Saldo insuficiente.'); } else { this.#saldo -= quantidade; console.log(`${this.dono} sacou ${quantidade}. Saldo atual: ${this.#saldo}`); } } // Método público para verificar o saldo verificarSaldo() { console.log(`O saldo de ${this.dono} é ${this.#saldo}`); } } // Criando uma instância da classe ContaBancario const conta = new ContaBancaria('Henrique', 1000); // Interagindo com a conta bancária conta.depositar(500); // Henrique depositou 500. Saldo atual: 1500 conta.retirar(200); // Henrique sacou 200. Saldo atual: 1300 conta.verificarSaldo(); // O saldo de Henrique é 1300 // Tentando acessar o campo privado '#saldo' diretamente (irá causar erro) // console.log(conta.#saldo); // SyntaxError: Private field '#saldo' must be declared in an enclosing class
- Definição da Classe:
class ContaBancaria
define uma nova classe chamadaContaBancaria
.- Campos Privados:
#saldo
é um campo privado, que não pode ser acessado diretamente fora da classe.- Construtor:
constructor(dono, saldo)
inicializa a instância da classe comdono
esaldo
.- Métodos Públicos:
depositar(quantidade)
,retirar(quantidade)
, everificarSaldo()
são métodos públicos que interagem com o campo privado#saldo
.- Instância da Classe:
const conta = new ContaBancaria('Henrique', 1000)
cria um novo objeto da classeContaBancaria
.- Interagindo com a Instância:
conta.depositar(500)
deposita dinheiro na conta.conta.retirar(200)
saca dinheiro da conta.conta.verificarSaldo()
verifica o saldo da conta.- Acesso Indevido:
console.log(conta.#saldo)
tenta acessar o campo privado diretamente, o que resulta em um erro de sintaxe.O polimorfismo permite que métodos com o mesmo nome se comportem de maneira diferente em classes diferentes. No exemplo abaixo, o método
greet
se comporta de maneira diferente nas classesPerson
eEmployee
.// Definindo a classe Programador que herda da classe Funcionario class Programador extends Funcionario { constructor(nome, idade, meuTrabalho, departamento) { // Chama o construtor da classe pai (Funcionario) super(nome, idade, meuTrabalho); this.departamento = departamento; // Atribui o valor do argumento 'departamento' à propriedade 'departamento' do objeto } // Sobrescrevendo o método apresentar da classe pai apresentar() { console.log(`Olá, meu nome é ${this.nome}, eu sou um(a) ${this.meuTrabalho} do departamento de ${this.departamento} e eu tenho ${this.idade} anos.`); } } // Criando uma instância da classe Programador const dev1 = new Programador('Rick', 22, 'Desenvolvedor', 'TI'); // Chamando o método de instância apresentar dev1.apresentar(); // Olá, meu nome é Rick, eu sou um(a) Desenvolvedor do departamento de TI e eu tenho 22 anos.
- Definição da Classe Derivada:
class Programador extends Funcionario
define uma nova classeProgramador
que herda da classeFuncionario
.- Construtor:
constructor(nome, idade, meuTrabalho, departamento)
inicializa a instância da classeProgramador
, chamandosuper(nome, idade, meuTrabalho)
para herdar as propriedadesnome
,idade
, emeuTrabalho
da classeFuncionario
.- Método Sobrescrito:
apresentar()
na classeProgramador
sobrescreve o métodoapresentar
da classeFuncionario
.- Instância da Classe:
const dev1 = new Programador('Rick', 22, 'Desenvolvedor', 'TI')
cria um novo objeto da classeProgramador
.- Chamando Métodos:
dev1.apresentar()
chama o métodoapresentar
sobrescrito na instânciadev1
.As classes em JavaScript fornecem uma maneira eficiente de criar e gerenciar objetos, promovendo a reutilização de código e a organização através de herança, encapsulamento e polimorfismo. Com a introdução de ES6, a sintaxe das classes tornou-se mais intuitiva e alinhada com outras linguagens de programação orientadas a objetos.
OBS: Você pode aprender mais lendo esse artigo: MDN - Classes
Iterators são uma forma de acessar elementos de uma coleção (como arrays, strings ou objetos personalizados) de forma sequencial, sem expor a estrutura interna da coleção. Em JavaScript, um objeto é considerado um iterator se ele implementa um método chamado
next()
, que retorna um objeto com duas propriedades:
value
: O próximo valor na sequência.done
: Um booleano indicando se a sequência terminou (true
) ou não (false
).Vamos criar um iterator simples para iterar sobre um array:
// Nosso array de exemplo const array = [1, 2, 3, 4, 5]; // Função para criar um iterator para o array function createIterator(arr) { let index = 0; return { next: function() { if (index < arr.length) { return { value: arr[index++], done: false }; } else { return { value: undefined, done: true }; } } }; } // Criando o iterator const iterator = createIterator(array); console.log(iterator.next()); // { value: 1, done: false } console.log(iterator.next()); // { value: 2, done: false } console.log(iterator.next()); // { value: 3, done: false } console.log(iterator.next()); // { value: 4, done: false } console.log(iterator.next()); // { value: 5, done: false } console.log(iterator.next()); // { value: undefined, done: true }Neste exemplo, a função
createIterator
cria um iterator para o array fornecido. Cada chamada paranext()
retorna o próximo valor do array até que todos os valores sejam retornados.
Generators são uma forma mais poderosa e flexível de criar iterators em JavaScript. Um generator é uma função que pode ser pausada e retomada, e sua execução é controlada por meio do uso da palavra-chave
yield
. As funções geradoras são definidas usando a sintaxefunction*
.Vamos criar um generator simples que produz valores de 1 a 5:
// Definindo uma função geradora function* simpleGenerator() { yield 1; yield 2; yield 3; yield 4; yield 5; } // Criando um generator const generator = simpleGenerator(); console.log(generator.next()); // { value: 1, done: false } console.log(generator.next()); // { value: 2, done: false } console.log(generator.next()); // { value: 3, done: false } console.log(generator.next()); // { value: 4, done: false } console.log(generator.next()); // { value: 5, done: false } console.log(generator.next()); // { value: undefined, done: true }Neste exemplo, a função geradora
simpleGenerator
usa a palavra-chaveyield
para produzir valores. Cada chamada paranext()
retorna o próximo valor produzido pela função geradora.Generators podem ser usados em loops para simplificar a iteração:
function* simpleGenerator() { yield 1; yield 2; yield 3; yield 4; yield 5; } // Criando um generator const generator = simpleGenerator(); // Usando um loop for...of para iterar sobre os valores for (const value of generator) { console.log(value); // 1, 2, 3, 4, 5 }Neste exemplo, o loop
for...of
itera automaticamente sobre os valores produzidos pelo generator.Generators podem ser usados para implementar lógica mais complexa, como produção de uma sequência infinita de números:
function* infiniteSequence() { let i = 0; while (true) { yield i++; } } // Criando um generator const generator = infiniteSequence(); // Pegando os primeiros 5 valores console.log(generator.next().value); // 0 console.log(generator.next().value); // 1 console.log(generator.next().value); // 2 console.log(generator.next().value); // 3 console.log(generator.next().value); // 4Neste exemplo, o generator
infiniteSequence
continua produzindo valores indefinidamente.Iterators e Generators são ferramentas poderosas em JavaScript para manipular coleções de dados de forma eficiente e flexível. Iterators fornecem uma interface padrão para acessar elementos de uma coleção sequencialmente, enquanto Generators permitem criar iterators complexos com menos código e maior clareza.
- Iterators: Implementam um método
next()
para acessar elementos sequencialmente.- Generators: Funções que podem ser pausadas e retomadas usando
yield
, facilitando a criação de iterators complexos.OBS: Você pode aprender mais lendo esse artigo: MDN - Iterators e Generators
Módulos são uma maneira de organizar e reutilizar código em JavaScript. Eles permitem dividir o código em partes menores e mais gerenciáveis, cada uma com sua própria funcionalidade. Existem dois principais sistemas de módulos em JavaScript: ESModules (ECMAScript Modules) e CommonJS.
ESModules é o sistema de módulos nativo do JavaScript, introduzido na especificação ECMAScript 2015 (ES6). Ele é suportado diretamente pelos navegadores modernos e também pelo Node.js.
- Importação e Exportação: Você pode exportar partes de um módulo usando a palavra-chave
export
e importá-las em outros módulos usandoimport
.- Escopo do Módulo: Cada módulo tem seu próprio escopo, o que significa que variáveis e funções definidas em um módulo não estão disponíveis globalmente.
- Carregamento Assíncrono: Os módulos podem ser carregados de forma assíncrona, o que melhora o desempenho.
Exportação:
Você pode exportar variáveis, funções, classes, etc., de um módulo. Existem duas maneiras principais de fazer isso: exportação nomeada e exportação padrão.
Exportação Nomeada:// arquivo: mathUtils.js // Exportação nomeada de funções export function add(a, b) { return a + b; } export function subtract(a, b) { return a - b; } // Exportação nomeada de uma variável export const PI = 3.14159;Exportação Padrão:
// arquivo: greetings.js // Exportação padrão de uma função export default function greet(name) { return `Olá, ${name}!`; }Importação:
Para usar as funcionalidades exportadas de outro módulo, você as importa.
Importação Nomeada:// arquivo: main.js import { add, subtract, PI } from './mathUtils.js'; console.log(add(2, 3)); // 5 console.log(subtract(5, 3)); // 2 console.log(PI); // 3.14159Importação Padrão:
// arquivo: main.js import greet from './greetings.js'; console.log(greet('Henrique')); // Olá, Henrique!Importação Combinada:
// arquivo: main.js import greet from './greetings.js'; import { add, subtract, PI } from './mathUtils.js'; console.log(greet('Rick')); // Olá, Rick! console.log(add(10, 20)); // 30 console.log(subtract(30, 10)); // 20 console.log(PI); // 3.14159Importação de Módulos Completos:
Você pode importar todo o módulo como um objeto e acessar suas exportações como propriedades desse objeto.// arquivo: main.js import * as math from './mathUtils.js'; console.log(math.add(2, 3)); // 5 console.log(math.subtract(5, 3)); // 2 console.log(math.PI); // 3.14159
CommonJS é o sistema de módulos usado pelo Node.js. Antes da introdução dos ESModules, era a maneira padrão de estruturar módulos em JavaScript para o ambiente de servidor.
- Importação e Exportação: Usam
require
para importar emodule.exports
para exportar.- Síncrono: O carregamento de módulos é síncrono, o que é adequado para o ambiente de servidor.
Exportação:
No CommonJS, você usamodule.exports
ouexports
para exportar funcionalidades de um módulo.// arquivo: mathUtils.js // Exportação de uma função usando module.exports module.exports.add = function(a, b) { return a + b; }; module.exports.subtract = function(a, b) { return a - b; }; // Exportação de uma variável usando module.exports module.exports.PI = 3.14159;Importação:
Para importar funcionalidades de outro módulo, você usarequire
.// arquivo: main.js const math = require('./mathUtils.js'); console.log(math.add(2, 3)); // 5 console.log(math.subtract(5, 3)); // 2 console.log(math.PI); // 3.14159Exportação de Módulo Inteiro:
Você pode exportar um módulo inteiro como um objeto.// arquivo: greetings.js function greet(name) { return `Olá, ${name}!`; } module.exports = greet;Importação de Módulo Inteiro:
// arquivo: main.js const greet = require('./greetings.js'); console.log(greet('Rick')); // Olá, Rick!
Característica ESModules CommonJS Palavra-chave de importação import
require
Palavra-chave de exportação export
module.exports
Escopo Cada módulo tem seu próprio escopo Cada módulo tem seu próprio escopo Suporte em Navegadores Sim Não Suporte em Node.js Sim (a partir da versão 12 com flag, nativo na versão 13 e posteriores) Sim Carregamento Assíncrono Síncrono OBS: Você pode aprender mais lendo esse artigo: MDN - Introdução ao ES Modules, MDN - Import, MDN - Export, MDN - CommonJS
A gestão de memória é um aspecto crítico da programação que garante que a memória usada pelos aplicativos seja alocada e liberada de forma eficiente. Em JavaScript, a gestão de memória é feita automaticamente, e uma parte essencial desse processo é o Garbage Collection.
O Garbage Collection é o processo automático pelo qual a memória que não é mais utilizada pelo programa é liberada para que possa ser reutilizada. Em JavaScript, o Garbage Collector identifica e remove objetos que não têm mais referências, evitando assim o vazamento de memória.
Alocação de Memória:
Quando você cria um objeto, uma variável ou qualquer estrutura de dados, a memória é alocada para armazená-los.let user = { name: "Henrique", age: 22 }; // Aloca memória para o objeto 'user'Referências e Escopo:
A memória continua alocada enquanto houver referências para os objetos. Quando um objeto não tem mais referências, ele se torna um candidato à coleta de lixo.let user = { name: "Henrique", age: 22 }; // O objeto 'user' tem uma referência user = null; // Agora o objeto 'user' não tem mais referência e pode ser coletadoAlgoritmos de Coleta:
JavaScript utiliza algoritmos como o Mark-and-Sweep e Generational Garbage Collection para identificar e liberar memória não utilizada.
- Mark-and-Sweep: Marca todos os objetos acessíveis e remove os não marcados.
- Generational: Divide os objetos em gerações para otimizar a coleta de objetos de curta duração.
function createObject() { let obj = { value: "Temporary" }; // Aloca memória para 'obj' return obj; } let temp = createObject(); // 'temp' agora refere-se ao objeto criado temp = null; // O objeto criado em 'createObject' agora pode ser coletado, já que 'temp' não o referencia maisO Ciclo de Vida da Memória descreve o processo de alocação e liberação da memória em um programa. Em JavaScript, esse ciclo é gerenciado automaticamente pelo motor de JavaScript, mas é útil entender as fases envolvidas.
Alocação:
Quando uma variável é declarada ou um objeto é criado, a memória é alocada para armazenar esses dados.let data = [1, 2, 3]; // Alocação de memória para o arrayUso:
O programa utiliza a memória alocada para armazenar e manipular dados.data.push(4); // Utilização do array 'data'Desreferenciação:
Quando uma variável ou objeto não é mais necessário e não tem mais referências, a memória se torna disponível para coleta.data = null; // 'data' agora é null, e o array pode ser coletado se não houver mais referênciasLiberação:
O Garbage Collector identifica os objetos sem referências e libera a memória.// O motor de JavaScript pode coletar o array 'data' a qualquer momento se não houver mais referências para ele
function processData() { let tempData = [1, 2, 3]; // Alocação de memória para 'tempData' tempData.push(4); // Uso do array return tempData; // 'tempData' ainda é referenciado } let result = processData(); // 'result' agora refere-se ao array retornado result = null; // 'result' não referencia mais o array, tornando-o candidato à coletaA gestão de memória em JavaScript é crucial para o desempenho e a estabilidade dos aplicativos. O Garbage Collection é um mecanismo automático que ajuda a liberar memória não utilizada, enquanto o Ciclo de Vida da Memória descreve as fases de alocação, uso, desreferenciação e liberação de memória.
OBS: Você pode aprender mais lendo esse artigo: MDN - Gerenciamento de memoria
Depuração de problemas envolve identificar e corrigir erros no seu código JavaScript. As ferramentas de desenvolvimento do Chrome são úteis para isso, permitindo que você inspecione o código, defina pontos de interrupção (breakpoints), e acompanhe a execução passo a passo.
- Abrir o DevTools:
- Pressione
F12
ouCtrl + Shift + I
(Windows/Linux) ouCmd + Option + I
(Mac).- Acessar o Console:
- A aba
Console
exibe mensagens de erro e log. Se o seu código tiver erros, eles aparecerão aqui.- Adicionar Breakpoints:
- Na aba
Sources
, você pode ver o código fonte do seu projeto. Clique no número da linha onde deseja adicionar um ponto de interrupção. A execução do código será pausada nesse ponto, permitindo inspeção.- Inspecionar Variáveis e Pilha de Chamadas:
- Quando o código é pausado em um breakpoint, você pode inspecionar o estado das variáveis na aba
Scope
. A abaCall Stack
mostra a pilha de chamadas, ajudando a entender como o código chegou ao ponto atual.- Passo a Passo (Step Through Code):
- Utilize os botões de
Step Over
(F10),Step Into
(F11) eStep Out
(Shift + F11) para executar o código linha por linha, entrar em funções, ou sair delas.function apresentar(nome) { let msg = "Olá, " + nome; console.log(msg); return msg; } apresentar("Henrique");
- Defina um breakpoint na linha
console.log(msg);
.- Ao executar o código, o DevTools pausará na linha do breakpoint, permitindo que você inspecione a variável
msg
e veja seu valor.Vazamentos de memória ocorrem quando a memória não é liberada corretamente, levando a um uso excessivo de memória e possíveis lentidões ou travamentos.
- Abrir o DevTools:
- Novamente, use
F12
ouCtrl + Shift + I
/Cmd + Option + I
.- Acessar a Aba
Performance
:
- Use a aba
Performance
para gravar e analisar o uso de memória. Clique emRecord
, execute as ações no seu site e pare a gravação.- Analisar a Análise de Heap (Heap Snapshot):
- Vá para a aba
Memory
e selecioneHeap snapshot
. Tire um instantâneo antes e depois de realizar uma ação que você suspeita estar causando o vazamento.- Compare os instantâneos para verificar se há objetos que não estão sendo liberados como esperado.
- Localizar Vazamentos:
- Analise os snapshots para encontrar objetos que estão sendo retidos desnecessariamente. Preste atenção nas referências que estão mantendo os objetos vivos.
let globalArray = []; function createLeak() { let localArray = new Array(1000000).fill("data"); globalArray.push(localArray); } createLeak();
- Execute o código e use a aba
Memory
para tirar snapshots antes e depois de chamarcreateLeak
.- Verifique se
globalArray
está retendo olocalArray
mesmo após a função ser executada, o que pode indicar um vazamento de memória.
A depuração de desempenho envolve identificar e resolver problemas que afetam a velocidade e a eficiência do seu código. Isso pode incluir tempos de carregamento lentos, animações que não estão suaves, e outros problemas de desempenho.
- Abrir o DevTools:
- Acesso via
F12
ouCtrl + Shift + I
/Cmd + Option + I
.- Acessar a Aba
Performance
:
- Na aba
Performance
, clique emRecord
e execute a ação que você deseja analisar. Pare a gravação quando terminar.- Analisar o Relatório:
- O DevTools fornece um relatório detalhado que inclui informações sobre eventos, execução de scripts, e atividades de layout.
- Verifique a linha do tempo para identificar onde o tempo está sendo gasto e procure por "long tasks" (tarefas longas) que podem estar causando lentidão.
- Analisar Painel de Flames (Flame Graph):
- O gráfico de chamas mostra onde o tempo está sendo gasto em chamadas de função. Funções com grandes barras podem ser os pontos críticos para otimização.
function intensiveTask() { let sum = 0; for (let i = 0; i < 10000000; i++) { sum += i; } return sum; } function main() { console.log(intensiveTask()); } main();
- Grave a execução da função
main
na abaPerformance
.- Analise o tempo gasto na função
intensiveTask
e considere otimizar a lógica ou dividir a tarefa em partes menores para melhorar o desempenho.OBS: Você pode aprender mais lendo esse artigo: firefox - devtools