Em JavaScript existem três formas de declarar variáveis: var, let e const. Hoje em dia, var é considerada obsoleta — tem comportamento imprevisível ligado ao hoisting e não respeita blocos.
// ❌ Evitar — escopo de função, hoisting estranho
var nome = "Ana";
// ✅ Usar — reatribuível, escopo de bloco
let contador = 0;
contador = 1; // OK
// ✅ Preferir sempre que possível — imutável (referência)
const PI = 3.14159;
// PI = 3; → TypeError!
const por defeito. Só usa let quando precisas de reatribuir o valor. Nunca uses var.
JavaScript tem 7 tipos primitivos. Os mais usados são string, number e boolean.
const nome = "João"; // string
const idade = 28; // number
const ativo = true; // boolean
const preco = 9.99; // number (decimais também)
const vazio = null; // null
let indefinido; // undefined
// Verificar tipo:
typeof nome; // "string"
typeof idade; // "number"
typeof ativo; // "boolean"
Strings têm dezenas de métodos úteis. Aqui estão os que vais usar mais frequentemente.
const frase = "Olá, Mundo!";
frase.length; // 11
frase.toUpperCase(); // "OLÁ, MUNDO!"
frase.toLowerCase(); // "olá, mundo!"
frase.includes("Mundo"); // true
frase.startsWith("Olá"); // true
frase.replace("Mundo", "JS"); // "Olá, JS!"
frase.split(", "); // ["Olá", "Mundo!"]
frase.trim(); // remove espaços no início/fim
frase.slice(4, 9); // "Mundo"
// Template literals (backticks)
const msg = `Olá, ${frase}! Tens ${28} anos.`;
JavaScript tem apenas um tipo numérico: number. Inclui inteiros e decimais (vírgula flutuante).
// Operadores básicos
10 + 3; // 13
10 - 3; // 7
10 * 3; // 30
10 / 3; // 3.333...
10 % 3; // 1 (resto da divisão)
2 ** 8; // 256 (potência)
// Math — objeto com funções matemáticas
Math.round(4.6); // 5
Math.floor(4.9); // 4
Math.ceil(4.1); // 5
Math.abs(-7); // 7
Math.max(3, 7, 2); // 7
Math.random(); // 0.0 a 0.999...
// Cuidado com decimais!
(0.1 + 0.2).toFixed(1); // "0.3" (string!)
Condicionais permitem que o código tome decisões com base em valores.
const nota = 14;
// if / else if / else
if (nota >= 18) {
console.log("Muito Bom");
} else if (nota >= 14) {
console.log("Bom");
} else if (nota >= 10) {
console.log("Suficiente");
} else {
console.log("Negativo");
}
// Operador ternário — condição ? seVerdade : seFalso
const resultado = nota >= 10 ? "Passou" : "Reprovou";
// Operadores de comparação
5 === 5; // true (igualdade estrita — sempre usar!)
5 !== 3; // true
5 == "5"; // true (evitar — faz coerção de tipos)
=== (três iguais) em vez de ==. O operador == faz conversão de tipos automática que pode causar bugs inesperados.
Loops permitem repetir blocos de código. Cada tipo tem o seu caso de uso ideal.
// for clássico — quando sabes quantas iterações
for (let i = 0; i < 5; i++) {
console.log(i); // 0, 1, 2, 3, 4
}
// while — enquanto condição for verdade
let x = 10;
while (x > 0) {
console.log(x);
x -= 2;
}
// for...of — percorrer arrays (mais limpo)
const frutas = ["maçã", "banana", "laranja"];
for (const fruta of frutas) {
console.log(fruta);
}
// for...in — percorrer propriedades de objetos
const pessoa = { nome: "Ana", idade: 30 };
for (const chave in pessoa) {
console.log(chave, pessoa[chave]);
}
celsiusParaFahrenheit(c) que converte graus Celsius para Fahrenheit. Fórmula: F = C × 9/5 + 32function celsiusParaFahrenheit(c) {
// O teu código aqui
}
console.log(celsiusParaFahrenheit(0)); // 32
console.log(celsiusParaFahrenheit(100)); // 212
function celsiusParaFahrenheit(c) {
return c * 9 / 5 + 32;
}
classificarNota(n) que retorna "Muito Bom" (≥18), "Bom" (≥14), "Suficiente" (≥10) ou "Negativo" (<10).function classificarNota(n) {
// O teu código aqui
}
console.log(classificarNota(19)); // "Muito Bom"
console.log(classificarNota(15)); // "Bom"
console.log(classificarNota(8)); // "Negativo"
function classificarNota(n) {
if (n >= 18) return "Muito Bom";
if (n >= 14) return "Bom";
if (n >= 10) return "Suficiente";
return "Negativo";
}
tabuada(n) que usa um loop para imprimir a tabuada do número n de 1 a 10.function tabuada(n) {
// Usa um loop for
}
tabuada(7);
// 7 × 1 = 7
// 7 × 2 = 14
// ...
function tabuada(n) {
for (let i = 1; i <= 10; i++) {
console.log(`${n} × ${i} = ${n * i}`);
}
}
Existem duas formas principais de criar funções em JavaScript, com uma diferença importante: o hoisting.
// Declaração — sofre hoisting (pode chamar antes de declarar)
somar(2, 3); // funciona!
function somar(a, b) {
return a + b;
}
// Expressão — NÃO sofre hoisting
const multiplicar = function(a, b) {
return a * b;
};
// Arrow function (ES6+) — forma mais concisa
const dividir = (a, b) => a / b;
// Arrow com corpo de bloco
const potencia = (base, exp) => {
const resultado = base ** exp;
return resultado;
};
Funções podem ter valores por defeito para os parâmetros — muito útil para tornar argumentos opcionais.
// Sem valor por defeito — undefined se não for passado
function saudar(nome) {
console.log(`Olá, ${nome}!`);
}
saudar(); // "Olá, undefined!"
// Com valor por defeito (ES6+)
function saudar(nome = "visitante") {
console.log(`Olá, ${nome}!`);
}
saudar(); // "Olá, visitante!"
saudar("Ana"); // "Olá, Ana!"
// Rest params — número variável de argumentos
const somarTodos = (...nums) => nums.reduce((acc, n) => acc + n, 0);
somarTodos(1, 2, 3, 4); // 10
Em JavaScript, funções são cidadãs de primeira classe — podem ser guardadas em variáveis, passadas como argumento e retornadas de outras funções.
// Função como argumento (callback)
function executar(fn, valor) {
return fn(valor);
}
const dobro = n => n * 2;
executar(dobro, 5); // 10
// setTimeout usa callback
setTimeout(() => {
console.log("Passou 2 segundos!");
}, 2000);
// Função que retorna função (higher-order function)
function multiplicadorDe(n) {
return x => x * n;
}
const triplicar = multiplicadorDe(3);
triplicar(7); // 21
O escopo define onde uma variável é acessível. Um closure ocorre quando uma função interna lembra o escopo da função externa, mesmo após esta ter terminado.
// Escopo de bloco com let/const
{
let x = 10;
console.log(x); // 10
}
// console.log(x); → ReferenceError!
// Closure — a função "lembra" variáveis do contexto pai
function criarContador() {
let count = 0;
return () => {
count++;
return count;
};
}
const contador = criarContador();
contador(); // 1
contador(); // 2
contador(); // 3
useState — o estado "vive" dentro da closure do componente.
saudar(nome, hora) que retorna "Bom dia, [nome]!" se hora < 12, "Boa tarde" se < 19, "Boa noite" caso contrário.function saudar(nome, hora) {
// O teu código aqui
}
console.log(saudar("Ana", 9)); // "Bom dia, Ana!"
console.log(saudar("João", 15)); // "Boa tarde, João!"
function saudar(nome, hora) {
if (hora < 12) return `Bom dia, ${nome}!`;
if (hora < 19) return `Boa tarde, ${nome}!`;
return `Boa noite, ${nome}!`;
}
criarSaudador(saudacao) que retorna uma função que aceita um nome e usa a saudação fornecida.function criarSaudador(saudacao) {
// Retorna uma arrow function
}
const olá = criarSaudador("Olá");
const oi = criarSaudador("Oi");
olá("Maria"); // "Olá, Maria!"
oi("Pedro"); // "Oi, Pedro!"
function criarSaudador(saudacao) {
return nome => `${saudacao}, ${nome}!`;
}
Arrays são listas ordenadas de valores. Podem conter qualquer tipo de dado.
const nums = [1, 2, 3, 4, 5];
// Acesso por índice (começa em 0)
nums[0]; // 1
nums[nums.length - 1]; // 5 (último)
// Adicionar / remover
nums.push(6); // adiciona no fim → [1,2,3,4,5,6]
nums.pop(); // remove do fim → retorna 6
nums.unshift(0); // adiciona no início
nums.shift(); // remove do início
// Outros métodos
nums.includes(3); // true
nums.indexOf(3); // 2
nums.join(" - "); // "1 - 2 - 3 - 4 - 5"
nums.reverse(); // inverte (muta o array!)
nums.slice(1, 3); // [2, 3] (não muta)
Os três métodos funcionais mais importantes. Nunca mutam o array original — retornam sempre um novo valor.
const nums = [1, 2, 3, 4, 5];
// map — transforma cada elemento
const dobros = nums.map(n => n * 2);
// [2, 4, 6, 8, 10]
// filter — seleciona elementos
const pares = nums.filter(n => n % 2 === 0);
// [2, 4]
// reduce — acumula num único valor
const soma = nums.reduce((acc, n) => acc + n, 0);
// 15
// Encadear (chaining)
const resultado = nums
.filter(n => n % 2 !== 0) // [1,3,5]
.map(n => n * n) // [1,9,25]
.reduce((acc, n) => acc + n, 0); // 35
Objetos são coleções de pares chave-valor. São a estrutura de dados mais comum em JavaScript.
const pessoa = {
nome: "Ana",
idade: 28,
cidade: "Lisboa",
ativo: true
};
// Acesso — dot notation ou bracket notation
pessoa.nome; // "Ana"
pessoa["idade"]; // 28
// Adicionar / modificar / apagar
pessoa.email = "ana@mail.pt";
pessoa.idade = 29;
delete pessoa.ativo;
// Verificar se propriedade existe
"nome" in pessoa; // true
// Listar chaves / valores
Object.keys(pessoa); // ["nome", "idade", ...]
Object.values(pessoa); // ["Ana", 29, ...]
Object.entries(pessoa); // [["nome","Ana"], ...]
A desestruturação extrai valores de arrays e objetos de forma concisa. O spread (...) copia ou expande elementos.
// Desestruturação de objetos
const { nome, idade } = pessoa;
// Renomear
const { nome: nomeCompleto } = pessoa;
// Valor por defeito
const { pais = "Portugal" } = pessoa;
// Desestruturação de arrays
const [primeiro, segundo, ...resto] = [1, 2, 3, 4];
// primeiro=1, segundo=2, resto=[3,4]
// Spread — copiar objetos
const pessoaAtualizada = { ...pessoa, idade: 30 };
// Spread — juntar arrays
const todos = [...[1,2], ...[3,4]]; // [1,2,3,4]
// Desestruturação em parâmetros
function mostrar({ nome, idade }) {
console.log(`${nome} tem ${idade} anos`);
}
Array.map() para criar uma nova array onde cada número é o dobro do original.const numeros = [1, 2, 3, 4, 5];
const dobros = numeros.map(/* aqui */);
console.log(dobros); // [2, 4, 6, 8, 10]
const dobros = numeros.map(n => n * 2);
.filter() para retornar apenas as pessoas com 18 ou mais anos.const pessoas = [
{ nome: "Ana", idade: 17 },
{ nome: "João", idade: 24 },
{ nome: "Rita", idade: 15 },
{ nome: "Pedro", idade: 32 }
];
const adultos = pessoas.filter(/* aqui */);
const adultos = pessoas.filter(p => p.idade >= 18);
.reduce() para calcular o total de um carrinho de compras.const carrinho = [
{ produto: "Livro", preco: 12.99 },
{ produto: "Caneta", preco: 1.50 },
{ produto: "Caderno", preco: 5.99 }
];
const total = carrinho.reduce(/* aqui */, 0);
console.log(total.toFixed(2)); // "20.48"
const total = carrinho.reduce((acc, item) => acc + item.preco, 0);
JavaScript é single-threaded — executa uma coisa de cada vez. O event loop é o mecanismo que permite executar código assíncrono sem bloquear a thread principal.
console.log("1 - Início");
setTimeout(() => {
console.log("3 - setTimeout (async)");
}, 0); // mesmo com 0ms, vai para a fila!
console.log("2 - Fim do código síncrono");
// Output:
// 1 - Início
// 2 - Fim do código síncrono
// 3 - setTimeout (async)
fetch, setTimeout, leitura de ficheiros e eventos do DOM são assíncronos — não bloqueiam o código que vem a seguir.
Uma Promise representa um valor que ainda não está disponível, mas estará no futuro. Tem três estados: pending, fulfilled ou rejected.
// Criar uma Promise
const promessa = new Promise((resolve, reject) => {
const sucesso = true;
if (sucesso) {
resolve("Deu certo!");
} else {
reject(new Error("Algo correu mal"));
}
});
// Consumir com .then() / .catch() / .finally()
promessa
.then(resultado => console.log(resultado))
.catch(erro => console.error(erro.message))
.finally(() => console.log("Terminado"));
// Promise.all — esperar por várias promises
const resultados = await Promise.all([promessa1, promessa2]);
async/await é açúcar sintático sobre Promises. Torna o código assíncrono mais legível — parece código síncrono.
// Função assíncrona
async function buscarUtilizador(id) {
try {
const resposta = await fetch(
`https://jsonplaceholder.typicode.com/users/${id}`
);
if (!resposta.ok) {
throw new Error(`Erro HTTP: ${resposta.status}`);
}
const utilizador = await resposta.json();
return utilizador;
} catch (erro) {
console.error("Falhou:", erro.message);
return null;
}
}
// Usar
const user = await buscarUtilizador(1);
console.log(user.name);
esperar(ms) que retorna uma Promise que resolve após ms milissegundos.function esperar(ms) {
return new Promise(/* aqui */);
}
esperar(1000).then(() => {
console.log("Passou 1 segundo!");
});
function esperar(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function buscarPost(id) {
// URL: https://jsonplaceholder.typicode.com/posts/{id}
// Usa try/catch e verifica resposta.ok
}
buscarPost(1).then(p => console.log(p.title));
async function buscarPost(id) {
try {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
if (!res.ok) throw new Error(`Erro ${res.status}`);
return await res.json();
} catch (e) {
console.error(e.message);
return null;
}
}
O DOM (Document Object Model) representa o HTML como uma árvore de objetos que o JavaScript pode manipular.
// Selecionar um único elemento
const titulo = document.querySelector("h1");
const botao = document.querySelector("#meu-botao");
const card = document.querySelector(".card");
// Selecionar múltiplos elementos
const items = document.querySelectorAll("li");
items.forEach(item => console.log(item.textContent));
// Métodos clássicos (ainda úteis)
document.getElementById("app");
document.getElementsByClassName("card");
Depois de selecionar um elemento, podes alterar o seu conteúdo, atributos e estilos.
const el = document.querySelector("#titulo");
// Conteúdo
el.textContent = "Novo título"; // só texto
el.innerHTML = "<em>Itálico</em>"; // com HTML
// Atributos
el.setAttribute("data-id", "42");
el.getAttribute("data-id"); // "42"
el.href = "https://exemplo.pt";
// Classes CSS
el.classList.add("ativo");
el.classList.remove("ativo");
el.classList.toggle("ativo");
el.classList.contains("ativo"); // true/false
// Estilos inline
el.style.color = "red";
el.style.display = "none";
Eventos permitem que o JavaScript reaja a interações do utilizador como cliques, teclas e movimentos do rato.
const btn = document.querySelector("#btn");
// Adicionar listener
btn.addEventListener("click", (evento) => {
console.log("Clicado!", evento.target);
});
// Eventos comuns
// "click", "dblclick", "mouseover", "mouseout"
// "keydown", "keyup", "input", "change"
// "submit", "focus", "blur", "scroll"
// Prevenir comportamento padrão
form.addEventListener("submit", (e) => {
e.preventDefault(); // evita reload da página
// processar formulário
});
// Remover listener
const handler = () => console.log("clique");
btn.addEventListener("click", handler);
btn.removeEventListener("click", handler);
Podes criar novos elementos HTML com JavaScript e adicioná-los ao DOM em qualquer momento.
// Criar elemento
const li = document.createElement("li");
li.textContent = "Novo item";
li.classList.add("item");
// Adicionar ao DOM
const lista = document.querySelector("ul");
lista.appendChild(li); // no fim
lista.prepend(li); // no início
// innerHTML — criar múltiplos elementos
lista.innerHTML = `
<li>Primeiro</li>
<li>Segundo</li>
`;
// Remover elemento
li.remove();
// Exemplo prático — lista de tarefas
function adicionarTarefa(texto) {
const li = document.createElement("li");
li.textContent = texto;
li.addEventListener("click", () => li.remove());
lista.appendChild(li);
}
<!-- HTML -->
<p id="valor">0</p>
<button id="inc">+</button>
<button id="dec">-</button>
<button id="reset">Reset</button>
// JS — implementa a lógica aqui
let count = 0;
const display = document.querySelector("#valor");
// Adiciona os event listeners...
let count = 0;
const display = document.querySelector("#valor");
document.querySelector("#inc").addEventListener("click", () => {
display.textContent = ++count;
});
document.querySelector("#dec").addEventListener("click", () => {
display.textContent = --count;
});
document.querySelector("#reset").addEventListener("click", () => {
count = 0;
display.textContent = count;
});
Template literals (backticks) permitem interpolação de variáveis e strings multilinha de forma elegante.
const nome = "Ana";
const idade = 28;
// Interpolação
const msg = `Olá, ${nome}! Tens ${idade} anos.`;
// Expressões dentro de ${}
const res = `${2 + 2} é o resultado`; // "4 é o resultado"
// String multilinha
const html = `
<div class="card">
<h2>${nome}</h2>
<p>${idade} anos</p>
</div>
`;
// Chamada de função dentro
const maiusc = `Nome: ${nome.toUpperCase()}`;
Classes são açúcar sintático sobre o sistema de protótipos do JavaScript. Tornam o código orientado a objetos mais legível.
class Animal {
constructor(nome, especie) {
this.nome = nome;
this.especie = especie;
}
apresentar() {
return `Sou ${this.nome}, um ${this.especie}`;
}
static criar(nome, especie) {
return new Animal(nome, especie);
}
}
// Herança
class Cao extends Animal {
constructor(nome) {
super(nome, "cão"); // chama o construtor pai
}
ladrar() {
return "Au au!";
}
}
const rex = new Cao("Rex");
rex.apresentar(); // "Sou Rex, um cão"
rex.ladrar(); // "Au au!"
Módulos ES6 permitem dividir o código em ficheiros reutilizáveis com export e import.
// utils.js — exportar
export const PI = 3.14159;
export function somar(a, b) {
return a + b;
}
export default function principal() {
return "função principal";
}
// main.js — importar
import principal from "./utils.js"; // default
import { PI, somar } from "./utils.js"; // named
import * as utils from "./utils.js"; // tudo
import { somar as add } from "./utils.js"; // renomear
Duas funcionalidades modernas que tornam o acesso a dados aninhados muito mais seguro e conciso.
const user = {
nome: "Ana",
endereco: {
cidade: "Lisboa"
}
};
// Optional Chaining (?.) — não lança erro se undefined
user?.nome; // "Ana"
user?.endereco?.rua; // undefined (sem erro!)
user?.telefone?.numero; // undefined
user?.metodo?.(); // chama se existir
// Nullish Coalescing (??) — valor por defeito
// Só substitui null e undefined (não 0 ou "")
user?.pais ?? "Portugal"; // "Portugal"
0 ?? "padrão"; // 0 (diferente de ||)
0 || "padrão"; // "padrão" (0 é falsy)
// Combinado — muito comum em React
const cidade = user?.endereco?.cidade ?? "Desconhecida";
ContaBancaria com saldo inicial, métodos depositar(), levantar() (sem saldo negativo) e verSaldo().class ContaBancaria {
constructor(titular, saldoInicial = 0) {
// O teu código aqui
}
depositar(valor) { /* ... */ }
levantar(valor) { /* ... */ }
verSaldo() { /* ... */ }
}
const conta = new ContaBancaria("Ana", 100);
conta.depositar(50);
conta.levantar(200); // "Saldo insuficiente"
conta.verSaldo(); // "Ana: 150€"
class ContaBancaria {
constructor(titular, saldoInicial = 0) {
this.titular = titular;
this.saldo = saldoInicial;
}
depositar(valor) {
this.saldo += valor;
}
levantar(valor) {
if (valor > this.saldo) return console.log("Saldo insuficiente");
this.saldo -= valor;
}
verSaldo() {
console.log(`${this.titular}: ${this.saldo}€`);
}
}