JavaScript: conceitos-chave para brilhar na entrevista
javascript
Faaala, Dev! Está se preparando para aquela entrevista técnica e quer garantir que vai brilhar ao responder perguntas sobre JavaScript? Então você está no lugar certo! Neste artigo, vamos explorar os conceitos mais relevantes do JavaScript, aqueles que são fundamentais em entrevistas e que podem transformar o jeito como você aborda a linguagem. Preparado? Bora codar e descomplicar!

Por que JavaScript é tão importante nas entrevistas?

O JavaScript é uma das linguagens mais usadas no mundo da programação. Seja no front-end, no back-end ou até no desenvolvimento de aplicativos móveis, ele está presente. Nas entrevistas técnicas, os recrutadores procuram candidatos que não apenas saibam escrever código, mas que também compreendam os conceitos fundamentais da linguagem. Isso mostra domínio e confiança.
📽️
Veja o vídeo:
Video preview

Os conceitos que você precisa dominar

Vamos direto ao ponto com os principais conceitos que podem aparecer em uma entrevista:

Hoisting

O hoisting no JavaScript ocorre quando as declarações de variáveis e funções são "movidas" para o topo do escopo antes da execução do código. Entretanto, apenas as declarações são elevadas; as inicializações permanecem no local onde foram escritas. Isso pode levar a comportamentos inesperados, principalmente com variáveis declaradas com var.
console.log(nome); // undefined, devido ao hoisting da declaração, mas não da inicialização var nome = "Rocketseat"; saudacao(); // Chamada da função antes da sua declaração, funciona devido ao hoisting function saudacao() { console.log("Olá!"); }
Por outro lado, as declarações com let e const também são afetadas pelo hoisting, mas entram em um "tempo morto" temporal (temporal dead zone), onde o acesso à variável antes da sua declaração resulta em um erro.
console.log(nome); // ReferenceError: Cannot access 'nome' before initialization let nome = "Rocketseat";
Dica de entrevista: explique como evitar problemas relacionados ao hoisting utilizando let e const para garantir um escopo mais previsível e seguro.

Arrow functions

Introduzidas no ES6, as arrow functions oferecem uma sintaxe mais concisa para declarar funções. Elas são particularmente úteis para funções curtas e sem complexidade.
const somar = (a, b) => a + b; // Declaração de uma arrow function que soma dois números console.log(somar(2, 3)); // Saída: 5
Características principais:
  1. Sintaxe concisa: menos código, especialmente para funções de uma única linha.
  1. Herança de this: arrow functions não possuem seu próprio this. Em vez disso, herdam o valor de this do contexto léxico onde foram definidas.
const contador = { valor: 0, incrementar: function () { const aumentar = () => this.valor++; aumentar(); }, }; contador.incrementar(); console.log(contador.valor); // 1
Cuidado: por não terem seu próprio this, as arrow functions podem ser inadequadas para métodos de objetos que dependem de this.
Dica de entrevista: ressalte a diferença entre arrow functions e funções tradicionais, explicando os cenários em que cada uma é mais apropriada.

Contexto do this

O valor de this no JavaScript é determinado pelo contexto em que a função é chamada. Ele pode variar dependendo de onde e como a função é invocada. Em funções normais, o valor de this pode apontar para:
  • O objeto global (em chamadas no escopo global);
  • O objeto pai (em métodos de objetos);
  • Um valor específico quando utilizado com bind, call ou apply.
const pessoa = { nome: "Rocket", apresentar() { console.log(`Oi, eu sou o ${this.nome}`); }, }; pessoa.apresentar(); // Oi, eu sou o Rocket const apresentar = pessoa.apresentar; apresentar(); // undefined, pois 'this' não está mais referenciando o objeto 'pessoa'
Entretanto, arrow functions não possuem seu próprio this. Elas herdam o valor de this do contexto léxico onde foram definidas.
const pessoa = { nome: "Rocket", apresentar: () => { console.log(`Oi, eu sou o ${this.nome}`); // undefined }, }; pessoa.apresentar();
Dica de ouro: compare e contraste o comportamento de this em funções tradicionais e em arrow functions para destacar como elas se diferenciam.
📽️
Veja o vídeo:
Video preview

Closures

Uma closure é criada quando uma função interna "lembra" das variáveis do escopo da função externa, mesmo após a função externa ter sido executada. Isso é útil para criar funcionalidades como encapsulamento e armazenamento de estados.
function contador() { let count = 0; return function () { count++; // Incrementa o contador return count; // Retorna o valor atualizado de 'count' }; } const meuContador = contador(); console.log(meuContador()); // Saída: 1 console.log(meuContador()); // Saída: 2
Aqui, a função interna preserva o acesso à variável count mesmo após a execução de contador.
Aplicação prática:
  1. Criar funções privadas para proteger dados sensíveis.
  1. Implementar memoization para otimizar cálculos caros, reutilizando resultados já computados.
function memorizar(func) { const cache = {}; return function (arg) { if (cache[arg]) { return cache[arg]; } cache[arg] = func(arg); return cache[arg]; }; } const quadrado = memorizar((n) => n * n); console.log(quadrado(4)); // Calcula e armazena: 16 console.log(quadrado(4)); // Retorna do cache: 16
Dica: explique como closures ajudam a implementar encapsulamento em JavaScript, simulando comportamento de "private variables".

Escopo

O escopo no JavaScript define onde as variáveis, funções e objetos são acessíveis no código. Existem dois tipos principais:
  • Escopo global: variáveis declaradas fora de qualquer função ou bloco estão disponíveis em toda a aplicação.
  • Escopo local: variáveis declaradas dentro de uma função ou bloco só estão acessíveis naquele contexto.
function exemplo() { let local = "Visível apenas aqui"; console.log(local); // Visível apenas aqui } console.log(local); // ReferenceError
Detalhe importante:
  • var: escopo baseado na função. Não respeita blocos (como loops e condicionais).
  • let e const: escopo baseado no bloco. São mais seguras e evitam problemas com reatribuições indesejadas.
if (true) { var global = "Acessível fora do bloco"; let local = "Apenas dentro do bloco"; } console.log(global); // Acessível fora do bloco console.log(local); // ReferenceError
Dica de entrevista: explique como o uso de let e const previne erros comuns, como a redefinição acidental de variáveis em escopos diferentes.
📽️
Veja o vídeo:
Video preview

Prototypes e herança

O JavaScript utiliza herança prototípica, um modelo onde objetos podem herdar propriedades e métodos de outros objetos. Cada objeto tem uma propriedade chamada __proto__, que aponta para o protótipo de onde herda métodos e propriedades.
function Animal(nome) { this.nome = nome; } Animal.prototype.falar = function () { console.log(`${this.nome} faz um som.`); }; const cachorro = new Animal("Buddy"); cachorro.falar(); // Buddy faz um som.
Com o ES6, o uso de classes torna a sintaxe mais intuitiva, mas ainda utiliza o sistema de protótipos por baixo dos panos.
class Animal { constructor(nome) { this.nome = nome; } falar() { console.log(`${this.nome} faz um som.`); } } const gato = new Animal("Miau"); gato.falar(); // Miau faz um som.
Dica de entrevista: compare o modelo de protótipos com o uso de classes ES6 e explique como elas são apenas açúcar sintático para facilitar a leitura e escrita de código.

Event loop

O event loop é um mecanismo central no JavaScript que permite a execução não bloqueante de operações assíncronas. Ele coordena a interação entre a Call Stack, a Web APIs, a Task Queue e a Microtask Queue.
Componentes Principais:
  • Call Stack (Pilha de Chamadas): Onde as funções são empilhadas quando chamadas e desempilhadas quando retornam. Executa código JavaScript de forma síncrona.
  • Web APIs: Fornecem funcionalidades assíncronas (como setTimeout, fetch, eventos de DOM) que não são executadas na Call Stack, mas no ambiente do navegador ou Node.js.
  • Task Queue (Fila de Tarefas): Armazena as macrotarefas, como callbacks de setTimeout, setInterval, e eventos do DOM.
  • Microtask Queue (Fila de Microtarefas): Armazena as microtarefas, como callbacks de Promises (funções passadas para .then() e .catch()), e MutationObserver.
Fluxo de Operação do Event Loop:
  1. Executa a Call Stack: O JavaScript começa executando o código síncrono na Call Stack.
  1. Processa a Microtask Queue: Após a Call Stack estar vazia, o Event Loop verifica a Microtask Queue e executa todas as microtarefas em ordem, antes de processar qualquer tarefa na Task Queue.
  1. Processa a Task Queue: Depois de esvaziar a Microtask Queue, o Event Loop pega a primeira tarefa da Task Queue e a coloca na Call Stack para execução.
  1. Repetição: O processo se repete, garantindo que as microtarefas tenham prioridade sobre as macrotarefas.
Exemplo Detalhado:
console.log("Início"); // 1. Executa e imprime "Início" setTimeout(() => { console.log("Timeout 1"); // 6. Agendado na Task Queue }, 0); Promise.resolve() .then(() => { console.log("Promise 1"); // 4. Agendado na Microtask Queue }) .then(() => { console.log("Promise 2"); // 5. Agendado na Microtask Queue }); console.log("Fim"); // 2. Executa e imprime "Fim" // Ordem de execução: // 1. "Início" // 2. "Fim" // 3. Microtasks: // a. "Promise 1" // b. "Promise 2" // 4. Task Queue: // a. "Timeout 1"
Explicação passo a passo:
  • Passo 1: console.log("Início"); é executado e "Início" é impresso.
  • Passo 2: setTimeout(..., 0); é chamado. O callback é enviado para a Task Queue com tempo mínimo (não exatamente zero).
  • Passo 3: Promise.resolve().then(...).then(...); As funções passadas para .then() são colocadas na Microtask Queue.
  • Passo 4: console.log("Fim"); é executado e "Fim" é impresso.
  • Passo 5: A Call Stack está vazia. O Event Loop verifica a Microtask Queue e executa "Promise 1" e "Promise 2" em ordem.
  • Passo 6: Após esvaziar a Microtask Queue, o Event Loop pega "Timeout 1" da Task Queue e o executa.
Diferença entre task queue e microtask queue:
  • Prioridade: microtasks têm prioridade sobre tasks. Ou seja, todas as microtarefas são executadas antes de qualquer macrotarefa.
  • Uso Comum:
    • Microtasks: Promises, process.nextTick() (Node.js), MutationObserver.
    • Tasks: setTimeout, setInterval, eventos de I/O, eventos do DOM.
Por que isso é importante?
Entender essa diferença é crucial para prever a ordem de execução em código assíncrono, evitando comportamentos inesperados.
Dica de entrevista: ao explicar o Event Loop, demonstre com exemplos como a priorização das microtarefas pode afetar a execução do código. Por exemplo, por que uma Promise é executada antes de um setTimeout com 0ms de delay.

Promises e Async/Await

As Promises permitem lidar com operações assíncronas de forma mais organizada e legível, substituindo o uso excessivo de callbacks.
fetch("https://api.example.com/dados") .then((resposta) => resposta.json()) .then((dados) => console.log(dados)) .catch((erro) => console.error(erro));
Com o ES8, o async/await simplificou ainda mais o fluxo assíncrono, tornando-o similar ao código síncrono.
async function buscarDados() { try { const resposta = await fetch("https://api.example.com/dados"); const dados = await resposta.json(); console.log(dados); } catch (erro) { console.error("Erro ao buscar dados", erro); } }
Dica de entrevista: explique como o uso de try/catch no async/await facilita o tratamento de erros em operações assíncronas complexas.

Map e filter

Os métodos map e filter são usados para transformar e filtrar arrays de forma funcional.
  • map: aplica uma função a cada elemento do array, retornando um novo array.
  • filter: retorna um novo array contendo apenas os elementos que passam em uma condição.
const numeros = [1, 2, 3, 4]; const dobrados = numeros.map((n) => n * 2); // [2, 4, 6, 8] const pares = numeros.filter((n) => n % 2 === 0); // [2, 4]
Dica de entrevista: mostre como map e filter ajudam a escrever código mais limpo e funcional.
📽️
Veja o vídeo:
Video preview

Reduce

O método reduce acumula valores de um array em um único resultado, utilizando um acumulador.
const numeros = [1, 2, 3, 4]; const soma = numeros.reduce((acc, n) => acc + n, 0); // 10
Ele também pode ser usado para transformar arrays em objetos.
const frutas = ["maçã", "banana", "maçã"]; const contagem = frutas.reduce((acc, fruta) => { acc[fruta] = (acc[fruta] || 0) + 1; return acc; }, {}); console.log(contagem); // { maçã: 2, banana: 1 }
Dica de entrevista: explique como reduce pode ser usado para resolver problemas mais complexos, como agregações de dados ou transformações de estruturas.

Destructuring

O destructuring permite que você extraia valores de objetos e arrays de maneira mais concisa e legível. Isso facilita o acesso direto às propriedades ou elementos, sem precisar usar repetidamente o nome do objeto ou índice do array.
const pessoa = { nome: "Rocketseat", idade: 5 }; const { nome, idade } = pessoa; console.log(nome); // Rocketseat console.log(idade); // 5
Também pode ser usado para arrays:
const numeros = [1, 2, 3]; const [primeiro, segundo] = numeros; console.log(primeiro); // 1 console.log(segundo); // 2
Extras com default values e renomeação: É possível atribuir valores padrão ou renomear as variáveis.
const pessoa = { nome: "Rocketseat" }; const { nome, idade = 10 } = pessoa; console.log(idade); // 10 const { nome: apelido } = pessoa; console.log(apelido); // Rocketseat
Dica: explique como o destructuring simplifica funções que recebem objetos como parâmetros.
📽️
Veja o vídeo:
Video preview

Spread e Rest

Os operadores spread (...) e rest (...) tornam o trabalho com arrays e objetos mais dinâmico, sendo amplamente utilizados para cópias, combinações e manipulações.

Spread: copiando e combinando

O operador spread expande elementos de arrays ou objetos.
const numeros = [1, 2, 3]; const novosNumeros = [...numeros, 4, 5]; console.log(novosNumeros); // [1, 2, 3, 4, 5]
Também funciona com objetos:
const pessoa = { nome: "Rocketseat", idade: 5 }; const novaPessoa = { ...pessoa, cidade: "São Paulo" }; console.log(novaPessoa); // { nome: "Rocketseat", idade: 5, cidade: "São Paulo" }
📽️
Veja o vídeo:
Video preview

Rest: coletando itens

O operador rest é usado para agrupar valores restantes em arrays ou objetos.
function somar(...numeros) { return numeros.reduce((total, num) => total + num, 0); } console.log(somar(1, 2, 3, 4)); // 10
Em objetos e arrays, ele agrupa o restante dos elementos:
const pessoa = { nome: "Rocketseat", idade: 5, cidade: "São Paulo" }; const { nome, ...restante } = pessoa; console.log(restante); // { idade: 5, cidade: "São Paulo" }
Dica: Explique como o uso de spread e rest evita mutações desnecessárias ao trabalhar com estruturas imutáveis.
📽️
Veja o vídeo:
Video preview

Falsy e truthy

No JavaScript, os valores são classificados como truthy ou falsy com base em seu comportamento em contextos booleanos.

Valores falsy

Os valores falsy são: false, 0, "", null, undefined, e NaN.
if (0) { console.log("Nunca será executado"); } else { console.log("Falsy"); }

Valores truthy

Todos os outros valores são considerados truthy, incluindo arrays ou objetos vazios.
if ("Rocketseat") { console.log("Valor truthy!"); // Executado }

Exemplo prático com validação

Use essa propriedade para validar entradas:
const input = ""; console.log(input || "Valor padrão"); // Valor padrão
Dica: Discuta como o JavaScript realiza coerção implícita ao determinar valores truthy ou falsy.
📄
Ficou curioso? Temos um artigo especial para você. Acesse aqui.

Manipulação do DOM

A manipulação do DOM (Document Object Model) permite interagir com a estrutura da página HTML, possibilitando criar experiências dinâmicas.
const botao = document.getElementById("botao"); botao.addEventListener("click", () => { alert("Botão clicado!"); });

Exemplo: alterando o conteúdo de um elemento

const titulo = document.querySelector("h1"); titulo.textContent = "Bem-vindo à Rocketseat!";

Exemplo: criando e adicionando elementos

const lista = document.querySelector("#lista"); const novoItem = document.createElement("li"); novoItem.textContent = "Novo item"; lista.appendChild(novoItem);
Dica: Ressalte a importância de evitar manipulações excessivas do DOM para otimizar a performance.
📽️
Veja o vídeo:
Video preview

Debouncing e Throttling

Essas técnicas ajudam a controlar a frequência com que uma função é executada, especialmente em eventos como scroll ou resize.

Debouncing

Debouncing garante que a função seja executada apenas uma vez, após o intervalo especificado sem novas chamadas.
function debounce(func, delay) { let timer; return function (...args) { clearTimeout(timer); timer = setTimeout(() => func(...args), delay); }; } const processarScroll = debounce(() => console.log("Scroll processado!"), 200); window.addEventListener("scroll", processarScroll);
📽️
Veja o vídeo:
Video preview

Throttling

Throttling garante que a função seja executada a cada intervalo fixo, ignorando chamadas intermediárias.
function throttle(func, limit) { let emExecucao = false; return function (...args) { if (!emExecucao) { func(...args); emExecucao = true; setTimeout(() => (emExecucao = false), limit); } }; } const processarResize = throttle(() => console.log("Resize processado!"), 200); window.addEventListener("resize", processarResize);
Dica: Discuta como essas técnicas ajudam a otimizar a performance da aplicação, reduzindo o número de chamadas desnecessárias.

Dicas para mandar bem na entrevista

Preparar-se para uma entrevista técnica vai além de saber escrever código: trata-se de demonstrar sua capacidade de resolver problemas, explicar conceitos e aplicar conhecimentos de forma prática. Aqui estão algumas dicas valiosas para transformar sua preparação em um desempenho brilhante:

Entenda o "porquê" dos conceitos

Não basta decorar definições ou exemplos. Compreenda profundamente o motivo por trás de cada conceito e como ele se encaixa no funcionamento da linguagem. Pergunte a si mesmo:
  • Por que o JavaScript tem hoisting?
  • Como o this varia dependendo do contexto?
  • Quando closures são realmente úteis?
Entender o contexto e os casos de uso ajuda a estruturar respostas claras e demonstra domínio durante a entrevista.

Pratique com projetos reais

Aprender conceitos sem aplicá-los pode limitar sua confiança. Projete soluções para problemas do dia a dia, como:
  • Criar uma to-do list usando closures para gerenciar tarefas.
  • Implementar um debounce para otimizar eventos de rolagem.
  • Construir um jogo simples, como Pedra, Papel e Tesoura, para explorar lógica condicional.
Além disso, refaça desafios práticos para simular como seria aplicar esses conceitos em um ambiente profissional.
📽️
Veja o vídeo:
Video preview

Desenvolva respostas concisas e impactantes

Em uma entrevista, o tempo é precioso. Pratique responder perguntas de forma clara e direta, mas sem abrir mão de um exemplo prático ou analogia. Por exemplo:
  • Para explicar hoisting, diga:
    • "O JavaScript 'eleva' declarações para o topo do escopo antes da execução, mas apenas declarações, não inicializações. Isso explica por que você pode acessar uma variável antes de declará-la com var, mas não com let ou const."
  • Use snippets curtos de código para reforçar sua explicação.

Entenda o contexto da vaga

Estude a stack tecnológica da empresa e destaque conhecimentos que se alinham ao que ela utiliza. Se a empresa usa frameworks como React, mencione como você aplicou arrow functions ou event loop em projetos front-end. Caso ela trabalhe com back-end em Node.js, destaque sua experiência com Promises, async/await e manipulação de APIs.
📽️
Veja o vídeo:
Video preview

Mostre que você resolve problemas

Os entrevistadores valorizam candidatos que conseguem identificar problemas e propor soluções. Quando perguntarem sobre algo, responda de forma estruturada:
  1. Identifique o problema: explique o contexto.
  1. Descreva a solução: mostre o raciocínio por trás de sua abordagem.
  1. Adicione valor: diga como sua solução impacta a performance, a legibilidade ou a manutenibilidade do código.
📽️
Veja o vídeo:
Video preview

Simule a entrevista

Simulações ajudam a construir confiança. Peça para um amigo ou mentor técnico te fazer perguntas, como:
  • "Explique o que são closures e dê um exemplo prático."
  • "Como o JavaScript gerencia tarefas assíncronas no event loop?"
  • "Qual a diferença entre map, filter e reduce?"
Grave suas respostas, analise e refine para melhorar a clareza e a fluidez.
📽️
Veja o vídeo:
Video preview

Demonstre interesse e curiosidade

Seja proativo ao demonstrar como você se mantém atualizado. Mencione projetos paralelos, cursos como os da Rocketseat ou sua participação em comunidades dev. Isso reflete não apenas sua paixão, mas também sua habilidade de aprender rapidamente — algo que as empresas valorizam.

Conclusão

Agora que você tem uma base sólida dos conceitos mais relevantes do JavaScript, é hora de aplicar, praticar e brilhar. Cada projeto, cada código e cada estudo adicional é um passo em direção ao domínio da linguagem e ao sucesso na entrevista.
Dica Final: A Rocketseat oferece uma série de cursos, como o Discover, que não apenas ensinam as bases do JavaScript, mas também desafiam você a aplicá-las em projetos reais. Complemente seu aprendizado e acelere sua evolução como dev.
💜
A preparação é o que transforma sonhos em conquistas. Esteja confiante, demonstre seu valor e encare cada desafio como uma oportunidade de mostrar o melhor de você. A próxima vaga é sua!

Aprenda programação do zero e DE GRAÇA

No Discover você vai descomplicar a programação, aprender a criar seu primeiro site com a mão na massa e iniciar sua transição de carreira.

COMECE A ESTUDAR AGORA