Tipos de Dados e Suas Características Fundamentais
O JavaScript possui oito categorias de dados fundamentais: undefined, null, number, string, boolean, Symbol, BigInt e Object. Estes se dividem em dois grupos distintos: primitivos e de referência.
Os tipos primitivos armazenam seus valores diretamente na pilha de execução, ocupando espaço reduzido e com tamanho fixo. Já os objetos, por serem estruturas complexas de tamanho variável, residem na memória heap, enquanto a pilha mantém apenas um ponteiro para seu endereço real.
Na pilha, os dados seguem o princípio LIFO (Last In, First Out), enquanto a heap opera como uma fila de prioridade. Do ponto de vista do sistema operacional, a pilha gerencia automaticamente memória para variáveis locais e parâmetros de função, ao contrário da heap que requer gerenciamento manual, embora o coletor de lixo do JavaScript libere memória não utilizada automaticamente.
Mecanismos de Verificação de Tipos
Existem quatro abordagens principais para identificar tipos em JavaScript:
typeof- Retorna uma string representando o tipo, porém classifica arrays, objetos enullcomo'object'instanceof- Verifica se um objeto possui em sua cadeia de protótipos a propriedadeprototypede uma função construtoraconstructor.name- Acessa o nome da função construtora, mas falha paranulleundefinedObject.prototype.toString.call()- Método robusto que retorna[object Tipo]para qualquer valor
Identificação Específica de Arrays
Para determinar se um valor é um array, utilize:
// Método mais confiável
Array.isArray([1, 2, 3]) // true
// Verificação via protótipo
Object.prototype.toString.call([]) === '[object Array]'
// Comparação direta de protótipo
[].__proto__ === Array.prototype
// Utilizando isPrototypeOf
Array.prototype.isPrototypeOf([])
Distinção Crucial: null vs undefined
Ambos representam ausência de valor, porém em contextos diferentes. undefined indica que uma variável foi declarada mas não inicializada, enquanto null é uma atribuição explícita representando intencionalmente "nenhum objeto".
Curiosamente, typeof null retorna 'object' devido à implementação inicial do JavaScript, onde valores eram representados por um tipo de tag e um valor. null usava o ponteiro zero, que coincidentemente era a tag para objetos.
Em comparações, undefined == null retorna true (coerção de igualdade), mas undefined === null retorna false (tipos distintos).
Implementação Personalizada do Operador instanceof
function verificarInstancia(objeto, construtor) {
let prototipoAtual = Object.getPrototypeOf(objeto);
const prototipoAlvo = construtor.prototype;
while (prototipoAtual !== null) {
if (prototipoAtual === prototipoAlvo) {
return true;
}
prototipoAtual = Object.getPrototypeOf(prototipoAtual);
}
return false;
}
// Teste
function Animal() {}
const cachorro = new Animal();
console.log(verificarInstancia(cachorro, Animal)); // true
console.log(verificarInstancia(cachorro, Array)); // false
Obtenção Segura do Valor undefined
Como undefined não é uma palavra reservada e pode ser sobrescrito, utilize o operador void para garantir o valor genuíno:
const valorIndefinido = void 0; // Sempre retorna undefined real
Características do Valor NaN
NaN (Not-a-Number) é do tipo number, resultando de operações matemáticas inválidas. Sua característica única é que NaN !== NaN retorna true, exigindo Number.isNaN() para verificação confiável.
Diferença Entre isNaN() e Number.isNaN()
isNaN() força a conversão do argumento para número antes da verificação, causando falsos positivos. Number.isNaN() primeiro confirma se o valor é do tipo número, então verifica se é NaN, sendo mais rigoroso.
Regras de Coerção do Operador de Igualdade Solta (==)
Quando tipos divergem, o JavaScript aplica estas transformações:
- Se um operando é
nulle o outroundefined, retornatrue - Comparando número e string, a string converte para número
- Booleano comparado com qualquer tipo converte para número (
true→ 1,false→ 0) - Objeto comparado com primitivo converte o objeto para valor primitivo via
valueOf()outoString()
Conversões para String
Valores convertidos para string resultam em:
null→'null'undefined→'undefined'true/false→'true'/'false'- Números convertidos diretamente
- Symbols apenas via conversão explícita
- Objetos sem
toString()personalizado →'[object Object]'
Conversões para Número
Transformações para tipo numérico:
undefined→NaNnull→0true/false→1/0- String vazia →
0, string inválida →NaN - Arrays vazias →
0, arrays com um elemento numérico → esse número - Objetos →
NaN
Conversões para Booleano
Valores avaliados como false: undefined, null, NaN, 0, -0, ''. Todos os outros valores, incluindo objetos vazios e arrays vazios, são true.
Comportamento dos Operadores Lógicos || e &&
Ambos operam por curto-circuito, retornando o operando que determina o resultado:
// || retorna o primeiro valor truthy
const resultado1 = '' || 'padrão'; // 'padrão'
// && retorna o primeiro valor falsy
const resultado2 = 'valor' && null && 'outro'; // null
Comparação Entre Object.is(), === e ==
=== compara igualdade estrita sem coerção. == permite coerção de tipos. Object.is() comporta-se como ===, mas trata +0 e -0 como diferentes e considera NaN igual a NaN.
Tipos de Envoltório (Wrapper Types)
Primitivos não possuem métodos, mas o JavaScript automaticamente os "envolve" em objetos temporários quando acessamos propriedades:
const texto = 'olá';
console.log(texto.toUpperCase()); // 'OLÁ' (temporariamente envolvido em String object)
// Conversão explícita
const numObj = Object(42);
const numPrimitivo = numObj.valueOf();
Mecanismos de Conversão Implícita
Quando operações exigem tipos específicos, o JavaScript segue este algoritmo:
- Para objetos
Date: tentatoString(), depoisvalueOf() - Para outros objetos: tenta
valueOf(), depoistoString() - Operador
+com string converte todos para string - Operadores aritméticos (
-,*,/) convertem para número - Operador
!!converte para booleano - Comparações
<e>entre strings usam ordem alfabética, outros casos convertem para número
Quando o Operador + Realiza Concatenação?
Quando qualquer operando é string ou após conversão de objeto resulta em string:
console.log(1 + '2'); // '12'
console.log({}.toString() + 1); // '[object Object]1'
Proposta do BigInt
Number.MAX_SAFE_INTEGER limita precisão acima de 2^53-1. BigInt resolve isso, representando inteiros arbitrariamente grandes através do sufixo n:
const grande = 9007199254740993n;
console.log(grande + 1n); // 9007199254740994n
Object.assign vs Operador Spread: Cópia Superficial
Ambos realizam cópia rasa (shallow copy):
const original = { a: 1, b: { c: 2 } };
const copia1 = Object.assign({}, original);
const copia2 = { ...original };
copia1.b.c = 3;
console.log(original.b.c); // 3 (ambas referenciam o mesmo objeto interno)
Object.assign() copia propriedades enumeráveis de múltiplas fontes. O spread copia elementos iteráveis, ignorando propriedades herdadas.
Comparação Detalhada: var, let e const
| Característica | var | let | const |
|---|---|---|---|
| Hoisting | Sim, com inicialização undefined | Sim, mas sem inicialização (Zona Temporal Morta) | Sim, mas sem inicialização (Zona Temporal Morta) |
| Escopo | Função ou global | Bloco | Bloco |
| Redeclaração | Permitida | Proibida | Proibida |
| Valor inicial | Opcional | Opcional | Obrigatório |
| Reatribuição | Permitida | Permitida | Proibida |
| Propriedade window | Sim | Não | Não |
Mutabilidade de Objetos Const
const protege a referência, não o valor:
const usuario = { nome: 'Ana' };
usuario.nome = 'Maria'; // Permitido
usuario.idade = 25; // Permitido
// Proibido:
// usuario = { nome: 'João' }; // TypeError
Arrow Functions como Construtores
Tentar usar new em uma arrow function lança TypeError, pois elas não possuem prototype, this léxico fixo e não podem ser usadas como construtoras.
Diferenças Fundamentais: Arrow vs Function Regular
- Sintaxe concisa:
const soma = (a, b) => a + b; thisléxico, não vinculável viacall/apply/bind- Sem objeto
arguments(use rest parameters) - Sem
prototype - Não pode ser usada como construtora
- Não pode usar
yield
Contexto de this em Arrow Functions
O this é capturado no momento da definição, herdado do escopo envolvente:
const objeto = {
valor: 42,
regular: function() { console.log(this.valor); },
arrow: () => { console.log(this.valor); }
};
objeto.regular(); // 42
objeto.arrow(); // undefined (this é do escopo global)
Cenários de Uso do Operador Spread
Para objetos: clonagem rasa, mesclagem, sobrescrita de propriedades.
Para arrays: clonagem, concatenação, conversão de iteráveis.
Para funções: parâmetros rest.
// Mesclagem de objetos
const config = { ...configPadrao, ...configUsuario };
// Conversão de NodeList para array
const elementos = [...document.querySelectorAll('div')];
// Parâmetros rest
function somar(...numeros) { return numeros.reduce((a, b) => a + b); }
Funcionalidades do Proxy
Proxy intercepta operações básicas em objetos:
const alvo = { nome: 'Teste' };
const manipulador = {
get(objeto, propriedade) {
console.log(`Acessando ${propriedade}`);
return objeto[propriedade];
},
set(objeto, propriedade, valor) {
console.log(`Definindo ${propriedade} = ${valor}`);
objeto[propriedade] = valor;
return true;
}
};
const proxy = new Proxy(alvo, manipulador);
proxy.nome; // "Acessando nome"
proxy.idade = 30; // "Definindo idade = 30"
Desestruturação de Objetos e Arrays
Arrays desestruturam por posição:
const [primeiro, segundo, , quarto] = [10, 20, 30, 40];
// primeiro = 10, segundo = 20, quarto = 40
Objetos desestruturam por nome:
const { idade, nome } = { nome: 'Carlos', idade: 25 };
// nome = 'Carlos', idade = 25 (ordem irrelevante)
Extraindo Propriedades Aninhadas
Para acessar propriedades profundas:
const dados = { usuario: { perfil: { email: 'test@ex.com' } } };
const { usuario: { perfil: { email } } } = dados;
// Com alias
const { usuario: { perfil: { email: contato } } } = dados;
Parâmetros Rest
Captura argumentos restantes em um array:
function logar(nivel, ...mensagens) {
console.log(`[${nivel}]`, ...mensagens);
}
logar('INFO', 'Sistema', 'iniciado'); // [INFO] Sistema iniciado
Templates Literais e Processamento de Strings
Usando crases para strings multilinha e interpolação:
const nome = 'Maria';
const mensagem = `
Olá ${nome}!
Idade: ${20 + 5} anos
`;
Implementação do Operador new
function criarInstancia(construtor, ...parametros) {
const instancia = Object.create(construtor.prototype);
const resultado = construtor.apply(instancia, parametros);
return (resultado !== null && (typeof resultado === 'object' || typeof resultado === 'function'))
? resultado
: instancia;
}
// Uso
function Produto(nome, preco) {
this.nome = nome;
this.preco = preco;
}
const item = criarInstancia(Produto, 'Notebook', 3500);
Map vs Object: Escolha a Estrutura Correta
| Aspecto | Map | Object |
|---|---|---|
| Chaves | Qualquer tipo (objetos, funções) | Apenas strings ou symbols |
| Ordem | Preserva inserção | Não garantida |
| Tamanho | Propriedade size |
Manual via Object.keys() |
| Iteração | Direta (iterable) | Requer for...in ou métodos |
| Performance | Melhor para adições/remoções frequentes | Otizado para leitura |
Map vs WeakMap
WeakMap aceita apenas objetos como chaves, mantém referências fracas (permitindo garbage collection) e não é enumerável. Map mantém referências fortes e preserva ordem de inserção.
Objetos Nativos do JavaScript
Objetos globais pré-definidos: Object, Function, Array, String, Number, Boolean, Date, RegExp, Math, JSON, Promise, Proxy, Reflect, Map, Set, WeakMap, WeakSet, Symbol, BigInt.
Expressões Regulares Comuns
// Cor hexadecimal
const regexCor = /^#([0-9A-F]{6}|[0-9A-F]{3})$/i;
// Data YYYY-MM-DD
const regexData = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
// Email simples
const regexEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
JSON: Formatos e Limitações
JSON não suporta: undefined, Symbol, Function, RegExp, Map, Set, WeakMap, WeakSet, referências circulares.
const obj = { a: 1, b: undefined, c: function() {} };
console.log(JSON.stringify(obj)); // {"a":1} (propriedades inválidas omitidas)
Técnicas de Carregamento Tardio de Scripts
- Posicione
<script>no final do<body> - Use
deferpara carregar em paralelo e executar após parsing - Use
asyncpara carregar e executar assim que disponível (pode bloquear parsing) - Crie dinamicamente via JavaScript:
const script = document.createElement('script'); - Utilize Intersection Observer para carregar sob demanda
Objetos Tipo Array (Array-like)
Possuem propriedade length e índices numéricos, mas não métodos de Aray: arguments, NodeList, HTMLCollection.
Conversão de Array-like para Array
// Métodos modernos
const array1 = Array.from(arrayLike);
const array2 = [...arrayLike];
// Método clássico
const array3 = Array.prototype.slice.call(arrayLike);
Métodos Essenciais de Arrays
| Método | Propósito | Modifica original? |
|---|---|---|
map() |
Transforma elementos | Não |
filter() |
Filtra elementos | Não |
reduce() |
Reduz a um valor | Não |
forEach() |
Itera sem retorno | Pode modificar |
slice() |
Extrai subarray | Não |
splice() |
Adiciona/remove elementos | Sim |
find() |
Busca elemento | Não |
some()/every() |
Testa condições | Não |
Unicode e Codificações UTF
Unicode é o conjunto de caracteres; UTF-8, UTF-16, UTF-32 são formas de codificação. UTF-8 é variável (1-4 bytes), eficiente para ASCII. UTF-16 usa 2 ou 4 bytes, melhor para caracteres Asiáticos. UTF-32 usa 4 bytes fixos, mais simples mas menos eficiente.
Operadores Bitwise
// Verificar paridade
const ehPar = n => (n & 1) === 0;
// Trocar valores sem variável temporária
let x = 5, y = 10;
x ^= y; y ^= x; x ^= y; // x=10, y=5
// Múltiplo de 2 mais próximo
const arredondar = n => (n + (n >> 31)) & ~1;
Objeto Arguments e Iteração
arguments é array-like, sem métodos de Array:
function listar() {
// Converter e iterar
Array.from(arguments).forEach(item => console.log(item));
// Ou usar call
Array.prototype.forEach.call(arguments, item => console.log(item));
}
DOM vs BOM
DOM (Document Object Model) manipula conteúdo da página. BOM (Browser Object Model) fornece acesso ao navegador (window, navigator, location, history).
Codificação de URIs
encodeURI(): codifica URI completa, preserva caracteres como:,/,?,#encodeURIComponent(): codifica componentes, transformando todos os caracteres não alfanuméricosescape(): obsoleto, não codifica@e usa formato%uXXXXpara Unicode
Implementação Básica de AJAX
function requisitar(url, dados) {
return new Promise((resolver, rejeitar) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolver(JSON.parse(xhr.response));
} else {
rejeitar(xhr.statusText);
}
};
xhr.onerror = () => rejeitar(xhr.statusText);
xhr.send(JSON.stringify(dados));
});
}
Mecanismo de Içamento (Hoisting)
Funções e declarações var são movidas para o topo de seu escopo durante a fase de compilação, permitindo uso antes da declaração. let e const também são içadas, mas permanecem em "Zona Temporal Morta" até a declaração.
Chamadas de Cauda (Tail Calls)
Uma chamada de função na última linha de outra função permite otimização: o engine pode reutilizar o frame de stack atual, economizando memória. Ativada apenas em modo estrito no ES6.
'use strict';
function fatorial(n, acumulador = 1) {
if (n <= 1) return acumulador;
return fatorial(n - 1, n * acumulador); // Tail call otimizada
}
ES Modules vs CommonJS
| Característica | ES Modules | CommonJS |
|---|---|---|
| Sistema | Static (aálise em tempo de compilação) | Dynamic (runtime) |
| Sintaxe | import/export |
require/module.exports |
| Valores | Live bindings (somente leitura) | Cópia por valor |
| Contexto | Strict mode por padrão | Non-strict |
| Plataforma | Navegadores modernos | Node.js |
Operações Comuns com DOM
// Seleção
const elemento = document.querySelector('.classe');
const lista = document.querySelectorAll('div');
// Criação
const novo = document.createElement('p');
novo.textContent = 'Texto';
// Inserção
elemento.appendChild(novo);
// Remoção
elemento.remove();
// Clonagem
const clone = elemento.cloneNode(true); // deep clone
Modo Estrito (use strict)
Ativado por 'use strict'; no topo de arquivos ou funções:
- Proíbe variáveis globais implícitas
- Elimina
with thisem funções não vinculadas éundefined- Proíbe propriedades duplicadas em objetos
- Proíbe parâmetros duplicados
Verificação de Herança
function Pessoa() {}
const joao = new Pessoa();
// instanceof
console.log(joao instanceof Pessoa); // true
// Verificação do construtor
console.log(joao.constructor === Pessoa); // true
// Verificação de tipo primitivo
console.log(Object.prototype.toString.call(joao)); // [object Object]
Tipagem Forte vs Fraca
Linguagens fortemente tipadas exigem declaração explícita e não realizam coerção automática (ex: Java, C#). Linguagens fracamente tipadas (ex: JavaScript, Python) convertem tipos implicitamente quando necessário, oferecendo flexibilidade mas exigindo cuidado.
Linguagens Interpretadas vs Compiladas
Interpretadas (JavaScript, Python) são traduzidas e executadas linha por linha em runtime, oferecendo portabilidade mas menor performance. Compiladas (C++, Rust) são convertidas para código de máquina antes da execução, gerando binários específicos da plataforma com alta performance.
for...in vs for...of
| for...in | for...of |
|---|---|
| Itera sobre chaves (strings) | Itera sobre valores |
| Inclui propriedades herdadas | Apenas iteráveis próprios |
| Usado para objetos | Usado para arrays, strings, Map, Set |
Iterando Objetos com for...of
const obj = { a: 1, b: 2 };
// Chaves
for (const chave of Object.keys(obj)) {
console.log(chave);
}
// Valores
for (const valor of Object.values(obj)) {
console.log(valor);
}
// Pares [chave, valor]
for (const [chave, valor] of Object.entries(obj)) {
console.log(chave, valor);
}
Comparação: AJAX, Fetch e Axios
AJAX: Baseado em XMLHttpRequest, verboso, callback-based.
Fetch: API moderna, baseada em Promises, nativa, mas requer tratamento manual de erros e não cancela requisições.
Axios: Biblioteca baseada em Promises, com interceptadores, timeout, cancelamento, e suporte universal (navegador e Node.js).
Métodos de Iteração de Arrays
map(): Transformaçãofilter(): Filtragemreduce(): ReduçãoforEach(): Efeitos colateraissome()/every(): Testes condicionaisfind()/findIndex(): BuscaflatMap(): Mapeamento e achatamento
Diferença Entre forEach e map
forEach executa uma ação para cada elemento sem retorno. map transforma cada elemento e retorna um novo array do mesmo tamanho.
const nums = [1, 2, 3];
// forEach não retorna
const resultado1 = nums.forEach(x => x * 2); // undefined
// map retorna novo array
const resultado2 = nums.map(x => x * 2); // [2, 4, 6]
Protótipos e Cadeia de Protótipos
Cada função construtora tem uma propriedade prototype contendo métodos compartilhados. Instâncias possuem __proto__ apontando para este objeto. Ao acessar propriedades, o motor busca na instância, depois no protótipo, depois no protótipo do protótipo, formando a cadeia até null.
Modificação vs Sobrescrita de Protótipo
Modificação: Adiciona propriedades ao protótipo existente.
function Animal() {}
Animal.prototype.comer = function() { console.log('Comendo'); };
Sobrescrita: Substitui o protótipo completamente, quebrando constructor:
function Veiculo() {}
Veiculo.prototype = {
constructor: Veiculo, // Corrigir manualmente
mover() { console.log('Movendo'); }
};
Relações na Cadeia de Protótipos
function Exemplo() {}
const instancia = new Exemplo();
console.log(instancia.__proto__ === Exemplo.prototype); // true
console.log(instancia.__proto__.__proto__ === Object.prototype); // true
console.log(instancia.__proto__.__proto__.__proto__ === null); // true
Fim da Cadeia de Protótipos
A cadeia termina em null, acessível via Object.prototype.__proto__ ou Object.getPrototypeOf(Object.prototype).
Propriedades Próprias vs Herdadas
Use hasOwnProperty() para distinguir:
const obj = { proprio: 1 };
console.log(obj.hasOwnProperty('proprio')); // true
console.log(obj.hasOwnProperty('toString')); // false (herdado)
Conceito e Aplicações de Closures
Closures permitem funções internas acessarem variáveis de funções externas mesmo após a execução externa ter terminado.
function criarFabricaDeId() {
let id = 0;
return () => ++id;
}
const gerarId = criarFabricaDeId();
console.log(gerarId()); // 1
console.log(gerarId()); // 2
Usado para: dados privados, funções de fábrica, contadores, memoização.
Escopos e Cadeia de Escopos
Global: Acessível em todo o código. Função: Acessível apenas dentro da função. Bloco: Criado por let/const em {}.
A cadeia de escopos permite que funções internas acessem variáveis externas, formando uma hierarquia de busca do mais interno para o global.
Contexto de Execução
Criado em duas fases:
- Criação: Vincula
this, cria ambiente léxico e variável - Execução: Executa código, atribui valores
A pilha de contextos gerencia chamadas: global → funções → funções aninhadas, seguindo LIFO.
Comportamento do this
this é determinado pela chamada:
- Função regular: quem a chamou
- Método de objeto: o objeto
- Construtor: nova instância
- Arrow function: léxico (contexto de definição)
call/apply/bind: explicitamente definido
Diferença Entre call e apply
Ambos invocam função com this específico. call() recebe argumentos separados, apply() recebe array de argumentos.
function saudar(mensagem, pontuacao) {
console.log(`${mensagem}, ${this.nome}${pontuacao}`);
}
const pessoa = { nome: 'João' };
saudar.call(pessoa, 'Olá', '!'); // Olá, João!
saudar.apply(pessoa, ['Oi', '?']); // Oi, João?
Implementação de call, apply e bind
// Implementação de call
Function.prototype.invocar = function(contexto, ...args) {
const chave = Symbol('temp');
contexto[chave] = this;
const resultado = contexto[chave](...args);
delete contexto[chave];
return resultado;
};
// Implementação de apply
Function.prototype.aplicar = function(contexto, args) {
if (!Array.isArray(args)) throw new TypeError('Args deve ser array');
return this.invocar(contexto, ...args);
};
// Implementação de bind
Function.prototype.vincular = function(contexto, ...argsFixos) {
const funcaoOriginal = this;
return function(...args) {
return funcaoOriginal.invocar(contexto, ...argsFixos, ...args);
};
};
Abordagens de Programação Assíncrona
- Callbacks (callback hell)
- Promises (encadeamento
.then) - Async/await (sintaxe síncrona)
- Generators (
function*eyield)
Comparação: setTimeout, Promise e async/await
setTimeout agenda execução futura. Promise representa valor futuro. async/await é sintaxe sugar sobre Promises, permitindo código assíncrono com aparência síncrona.
Fundamentos de Promises
Promise é objeto representando estado eventual de operação assíncrona. Estados: pending → fulfilled (resolve) ou rejected (reject). Uma vez resolvida, o estado é imutável.
const promessa = new Promise((resolver, rejeitar) => {
setTimeout(() => resolver('Concluído'), 1000);
});
promessa
.then(valor => console.log(valor))
.catch(erro => console.error(erro))
.finally(() => console.log('Sempre executa'));
Métodos Estáticos de Promise
Promise.all(): Espera todas resolvidas (rejeita na primeira falha)Promise.race(): Resolve/rejeita na primeira a concluirPromise.allSettled(): Espera todas independente de sucesso/fracassoPromise.any(): Resolve na primeira bem-sucedida
Async/Await: Sintaxe Moderna
async function buscarDados() {
try {
const resposta = await fetch('/api/dados');
const dados = await resposta.json();
return dados;
} catch (erro) {
console.error('Falha:', erro);
}
}
async retorna Promise. await pausa execução até Promise resolver.
Vantagens do Async/Await Sobre Promises
- Código linear, mais legível
- Tratamento de erros com
try/catchfamiliar - Depuração mais intuitiva com stack traces completos
- Menos aninhamento que encadeamento
.then
Paralelismo vs Concorrência
Paralelismo: Execução simultânea real (múltiplos cores). Concorrência: Alternância rápida de tarefas (single-core), dando ilusão de simultaneidade.
Callback Hell e Soluções
Múltiplos callbacks aninhados criam código ilegível. Soluções: modularização, Promises, async/await.
// Callback hell
getData(a => {
getMoreData(a, b => {
getMoreData(b, c => {
console.log(c);
});
});
});
// Solução com async/await
const resultado = await getMoreData(await getMoreData(await getData()));
Timers e Animações
setTimeout: Executa uma vez após atraso. setInterval: Executa repetidamente. requestAnimationFrame: Sincronizado com taxa de atualização do navegador, ideal para animações suaves.
Padrões de Criação de Objetos
// Factory
function criarUsuario(nome, idade) {
return { nome, idade, saudar() { return `Olá, ${this.nome}`; } };
}
// Constructor
function Livro(titulo, autor) {
this.titulo = titulo;
this.autor = autor;
}
Livro.prototype.info = function() { return `${this.titulo} por ${this.autor}`; };
// Class (ES6)
class Carro {
constructor(marca) { this.marca = marca; }
acelerar() { console.log(`${this.marca} acelerando`); }
}
// Object.create
const prototipo = { saudar() { return 'Oi'; } };
const instancia = Object.create(prototipo);
Padrões de Herança
// Herança prototípica
function Animal(tipo) { this.tipo = tipo; }
function Cachorro(nome) { Animal.call(this, 'cão'); this.nome = nome; }
Cachorro.prototype = Object.create(Animal.prototype);
Cachorro.prototype.constructor = Cachorro;
// Herança com classes
class Animal {
constructor(tipo) { this.tipo = tipo; }
}
class Cachorro extends Animal {
constructor(nome) { super('cão'); this.nome = name; }
}
Coleta de Lixo (Garbage Collection)
Algoritmos principais:
- Mark-and-Sweep: Marca objetos alcançáveis a partir de raízes, remove não marcados
- Reference Counting: Conta referências; quando zero, coleta (vulnerável a ciclos)
Otimizações: reutilize objetos, limpe referências com null, evite criar funções em loops.
Causas Comuns de Vazamento de Memória
- Variáveis globais acidentais
- Timers não limpos (
setInterval) - Referências DOM em cache após remoção de elementos
- Closures capturando objetos grandes desnecessariamente
- Event listeners não removidos
Event Loop e Microtasks/Macrotasks
JavaScript é single-threaded. O event loop processa:
- Executa todas as tarefas síncronas
- Processa todas as microtasks (
Promise.then,queueMicrotask) - Executa uma macrotask (
setTimeout,setInterval, I/O) - Repete
Microtasks têm prioridade sobre macrotasks.
Alias em Desestruturação
const pessoa = { primeiroNome: 'Ana', idade: 30 };
const { primeiroNome: nome, idade: anos } = pessoa;
// nome = 'Ana', anos = 30