Skip to content
Fase 4Organização

Arrays, Funções e POO

Progresso da fase0%

A carregar…

Até aqui, cada variável guardava um valor sozinho. Nesta fase vais aprender a guardar coleções inteiras de valores em arrays, a evitar repetir código com funções, e a agrupar dados relacionados com objetos e classes — as três ferramentas que separam um script pequeno de uma aplicação real.

Prompt recomendado para esta fase

Estou na Fase 4 a aprender arrays, funções e programação orientada a objetos (objetos, classes) em JavaScript. Quando eu te mostrar um problema, ajuda-me primeiro a decidir se preciso de um array, de uma função, de um objeto, ou de uma combinação — antes de escrevermos qualquer código, explica-me porquê.

Conteúdos teóricos

5 temas

1. Arrays: coleções de valores

Guardar várias informações relacionadas numa única variável, numa ordem específica.

Um array é uma lista ordenada de valores, guardada numa única variável. Em vez de criares nome1, nome2, nome3 separadamente, guardas todos numa lista:

javascript
const frutas = ["maçã", "banana", "pera"];
console.log(frutas);        // ["maçã", "banana", "pera"]
console.log(frutas.length); // 3 — o número de elementos

Acesso por índice

Cada posição num array tem um índice, e — tal como viste no for na Fase 3 — a contagem começa em 0, não em 1:

javascript
console.log(frutas[0]); // "maçã"  — primeiro elemento
console.log(frutas[1]); // "banana"
console.log(frutas[2]); // "pera"  — último elemento
console.log(frutas[3]); // undefined — não existe, e o JavaScript não avisa

Este é o erro mais comum com arrays: tentar aceder a um índice que não existe não dá erro — dá undefined silenciosamente, o que só se torna um bug óbvio várias linhas mais tarde. Sempre que não tiveres a certeza se um índice existe, compara-o com array.length.

Um array declarado com const ainda pode mudar de conteúdo

Isto surpreende muita gente: const frutas = [...] impede-te de reatribuir frutas a um array totalmente novo, mas não impede alterar o seu conteúdo. Podes adicionar, remover ou modificar elementos livremente — porque, tecnicamente, a "caixa" (a variável) continua a apontar para a mesma lista, só o interior dela mudou.

javascript
const numeros = [1, 2, 3];
numeros.push(4);      // válido! [1, 2, 3, 4]
// numeros = [5, 6];  // inválido — isto sim reatribuiria a variável

Percorrer um array

Combina o que aprendeste na Fase 3: um for que vai de 0 até array.length - 1 visita cada elemento:

javascript
for (let i = 0; i < frutas.length; i++) {
  console.log(i, "→", frutas[i]);
}

Repara porque o limite é array.length e a condição é < (não <=): um array com 3 elementos tem índices 0, 1 e 2 — usar <= tentaria aceder ao índice 3, que não existe.

2. Métodos úteis de array

Funções embutidas que poupam-te de escrever manualmente tarefas comuns com listas.

Arrays em JavaScript vêm com métodos (funções já incluídas) para as tarefas mais comuns:

  • push(valor) — adiciona um elemento ao fim do array.
  • pop() — remove e devolve o último elemento.
  • includes(valor) — devolve true ou false consoante o valor existir no array.
  • indexOf(valor) — devolve a posição do valor, ou -1 se não existir.
  • join(separador) — junta todos os elementos numa única string, com o separador que escolheres.
  • forEach(funcao) — executa uma função para cada elemento, sem precisares de escrever o for manualmente.
javascript
const tarefas = ["Estudar", "Correr"];

tarefas.push("Cozinhar");
console.log(tarefas); // ["Estudar", "Correr", "Cozinhar"]

console.log(tarefas.includes("Correr")); // true
console.log(tarefas.indexOf("Cozinhar")); // 2

tarefas.forEach(function (tarefa) {
  console.log("Fazer:", tarefa);
});

forEach vs for tradicional

forEach é mais curto e mais legível quando só precisas de "fazer algo com cada elemento". O for tradicional continua a ser necessário quando precisas do índice para outra coisa além de aceder ao elemento (ex: parar a meio com break — o forEach não permite break), ou quando precisas de percorrer o array ao contrário.

Existem ainda métodos mais avançados como map e filter, que transformam um array noutro sem alterar o original — vais precisar deles quando começares a trabalhar com dados vindos de uma base de dados ou de uma API, mais à frente no plano. Por agora, os seis métodos acima cobrem a maioria dos problemas que vais resolver.

3. Funções: reutilizar lógica sem a repetir

Empacotar um conjunto de passos com um nome, para os poderes chamar sempre que precisares.

Uma função é um bloco de código com um nome, que podes "chamar" (executar) quantas vezes quiseres, com valores diferentes de cada vez. É a ferramenta que evita copiares e colares o mesmo código várias vezes.

javascript
function saudar(nome) {
  console.log("Olá, " + nome + "!");
}

saudar("Ana");
saudar("Bruno");
  • function — a palavra que declara uma função.
  • saudar — o nome da função (as mesmas regras de nomes das variáveis aplicam-se).
  • (nome) — os parâmetros: variáveis que a função recebe de fora, diferentes a cada chamada.
  • saudar("Ana") — uma chamada da função, com "Ana" como o valor real (o argumento) para o parâmetro nome.

return: devolver um resultado

Uma função pode simplesmente fazer algo (como o console.log acima), ou pode calcular e devolver um valor, usando return:

javascript
function somar(a, b) {
  return a + b;
}

const resultado = somar(3, 4);
console.log(resultado); // 7

Assim que o JavaScript encontra um return, sai imediatamente da função — qualquer código depois dele, dentro da mesma função, nunca é executado. Uma função sem return explícito devolve undefined automaticamente.

Porque funções tornam o código mais fácil de testar e de ler

Uma função com um nome bom (calcularTotal, validarEmail) documenta-se a si própria — quem lê o código sabe o que ela faz sem ter de ler o corpo inteiro. E como o comportamento está isolado num só sítio, testar ou corrigir um bug fica muito mais simples do que se a mesma lógica estivesse copiada em cinco sítios diferentes do programa.

4. Arrow functions: uma sintaxe mais curta

Uma forma alternativa de escrever funções, muito comum em código JavaScript moderno.

As arrow functions (funções seta) são uma sintaxe alternativa, mais compacta, para escrever funções — vais vê-las em praticamente todo o código JavaScript moderno, incluindo dentro de forEach.

javascript
// Função tradicional
function dobrar(numero) {
  return numero * 2;
}

// A mesma função, como arrow function
const dobrarSeta = (numero) => {
  return numero * 2;
};

// Se o corpo for só um return, podes simplificar ainda mais
const dobrarCurta = (numero) => numero * 2;

As três formas acima fazem exatamente a mesma coisa. A última versão, sem chavetas e sem return explícito, chama-se implicit return — funciona só quando o corpo inteiro da função é uma única expressão a devolver.

Onde vais usá-las mais

Arrow functions são especialmente comuns como argumento de métodos como forEach, tornando o código mais curto:

javascript
const numeros = [1, 2, 3];

numeros.forEach((numero) => {
  console.log(numero * 2);
});

Não precisas de escolher "uma forma para sempre" — programadores experientes usam ambas consoante o contexto. Para já, o mais importante é conseguires ler arrow functions no código de outras pessoas (e nos exemplos desta plataforma) sem estranhar a sintaxe.

5. Objetos e Classes: agrupar dados com comportamento

De listas de valores soltos para estruturas com propriedades nomeadas e ações próprias.

Um objeto agrupa várias propriedades relacionadas, cada uma com um nome, numa única estrutura:

javascript
const utilizador = {
  nome: "Marta",
  idade: 30,
  ativo: true,
};

console.log(utilizador.nome);  // "Marta"
console.log(utilizador.idade); // 30

Repara na diferença para um array: num array acedes por posição (frutas[0]); num objeto acedes por nome (utilizador.nome). Usa arrays quando a ordem importa e os itens são do mesmo tipo (uma lista de tarefas); usa objetos quando queres agrupar propriedades diferentes de uma mesma "coisa" (os vários dados de um utilizador).

Objetos podem ter funções lá dentro (métodos)

javascript
const conta = {
  saldo: 100,
  depositar(valor) {
    this.saldo += valor;
  },
};

conta.depositar(50);
console.log(conta.saldo); // 150

A palavra this, dentro de um método, refere-se ao próprio objeto que está a chamar o método — é como o objeto se refere a si mesmo.

Classes: um "molde" para criar vários objetos parecidos

Quando precisas de criar muitos objetos com a mesma estrutura (várias contas bancárias, vários utilizadores), uma classe funciona como um molde:

javascript
class ContaBancaria {
  constructor(titular, saldoInicial) {
    this.titular = titular;
    this.saldo = saldoInicial;
  }

  depositar(valor) {
    this.saldo += valor;
  }
}

const contaDaAna = new ContaBancaria("Ana", 100);
const contaDoBruno = new ContaBancaria("Bruno", 50);

contaDaAna.depositar(30);
console.log(contaDaAna.saldo);   // 130
console.log(contaDoBruno.saldo); // 50 — cada instância tem o seu próprio estado

O constructor corre automaticamente sempre que crias uma nova conta com new, e é onde defines os valores iniciais de cada propriedade. Repara que contaDaAna e contaDoBruno são objetos completamente independentes, mesmo vindo da mesma classe — depositar na conta da Ana não afeta a do Bruno. Este é o conceito central de Programação Orientada a Objetos (POO): moldes que descrevem estrutura e comportamento, e instâncias independentes criadas a partir deles.

Exercícios práticos

4 exercícios
Fácil

Exercício Fácil — Gestor de Lista de Compras

Cria, adiciona, remove e percorre uma lista de compras usando um array e os seus métodos.

FerramentasVS CodeTerminalNode.js

Objetivo: praticar a criação de um array e os métodos push, pop, includes e forEach.

Passos a realizar

  1. Cria lista-compras.js na pasta fase4.
  2. Declara const lista com um array de 3 produtos iniciais.
  3. Usa push para adicionar mais dois produtos.
  4. Usa includes para verificar se um produto específico está na lista, e imprime o resultado.
  5. Usa pop para remover o último produto adicionado.
  6. Usa forEach para imprimir a lista final, numerada (dica: forEach dá-te acesso ao índice como segundo parâmetro da função).

Dicas úteis

  • forEach aceita dois parâmetros na função: o elemento e o seu índice — útil para numerares a lista ao imprimir.
  • Confirma sempre o array com console.log depois de cada operação (push, pop) para veres a mudança passo a passo.

Erros comuns

  • Esquecer que pop() remove o último elemento, não um elemento específico à tua escolha.
  • Tentar usar índices negativos para aceder ao array — o JavaScript não suporta isso da mesma forma que outras linguagens.

As minhas dúvidas

Médio

Exercício Médio — Calculadora com Funções

Escreve quatro funções (somar, subtrair, multiplicar, dividir) e uma função principal que as combina.

FerramentasVS CodeTerminalNode.js

Objetivo: praticar declaração de funções, parâmetros, return, e chamar uma função a partir de outra.

Passos a realizar

  1. Cria calculadora.js na pasta fase4.
  2. Escreve quatro funções: somar(a, b), subtrair(a, b), multiplicar(a, b), dividir(a, b), cada uma com return do resultado.
  3. Na função dividir, adiciona uma verificação: se b for 0, faz return da string "Erro: divisão por zero" em vez de tentar dividir.
  4. Escreve uma quinta função, calcular(operacao, a, b), que recebe o nome da operação como texto ("somar", "subtrair", etc.) e chama a função certa consoante o valor recebido (usa if/else ou switch, da Fase 3).
  5. Testa a função calcular com as quatro operações, e também com "dividir" passando 0 como segundo valor, para confirmares que a proteção funciona.

Fluxo de funcionamento

  1. 1calcular(operacao, a, b)
  2. 2switch/if na operação
  3. 3Chama a função certa
  4. 4Devolve o resultado

Dicas úteis

  • Uma função pode chamar outra função à vontade — é exatamente isso que calcular() vai fazer.
  • Testa sempre o caso de erro (dividir por 0) tão cedo quanto o caso normal — é fácil esquecê-lo depois de tudo o resto já estar a funcionar.

Erros comuns

  • Esquecer o return dentro de uma das quatro funções básicas, fazendo-a devolver undefined em vez do resultado.
  • Comparar o parâmetro operacao com o nome errado de string (ex: "soma" em vez de "somar"), fazendo cair sempre no caso por defeito.

As minhas dúvidas

Médio

Exercício Médio — Catálogo de Produtos com Objetos

Cria um array de objetos "produto" e usa forEach para calcular o valor total do stock.

FerramentasVS CodeTerminalNode.js

Objetivo: combinar arrays e objetos — o padrão mais comum em aplicações reais, uma lista de "coisas", cada uma com várias propriedades.

Passos a realizar

  1. Cria catalogo.js na pasta fase4.
  2. Declara const produtos, um array com três objetos, cada um com as propriedades nome, preco e quantidade.
  3. Declara let valorTotal a começar em 0.
  4. Usa forEach para percorrer produtos e, para cada um, somar (preco vezes quantidade) a valorTotal.
  5. Dentro do mesmo forEach, imprime uma linha por produto no formato "nome: X unidades a Y€ cada".
  6. No fim, fora do forEach, imprime o valorTotal do catálogo inteiro.

Código de exemplo

catalogo.js
const produtos = [
  { nome: "Teclado", preco: 25, quantidade: 4 },
  { nome: "Rato", preco: 12, quantidade: 10 },
  { nome: "Monitor", preco: 150, quantidade: 2 },
];

let valorTotal = 0;

produtos.forEach((produto) => {
  // completa aqui: soma a valorTotal e imprime a linha do produto
});

console.log("Valor total do catálogo:", valorTotal);
Estrutura de partida

Dicas úteis

  • valorTotal tem de ser declarado fora do forEach com let — se o declarares lá dentro, ele reinicia a cada produto e perdes a soma acumulada.
  • Acede às propriedades de cada objeto dentro do forEach com produto.nome, produto.preco, etc.

Erros comuns

  • Usar const em vez de let para valorTotal, o que impede de o atualizar a cada volta.
  • Esquecer os parênteses em (preco * quantidade) ao somar a valorTotal, alterando a ordem das operações se houver mais contas na mesma linha.

As minhas dúvidas

Difícil

Exercício Difícil — Classe ContaBancária

Cria uma classe completa com constructor, métodos, validações e um pequeno histórico de operações.

FerramentasVS CodeTerminalNode.js

Este exercício fecha a fase com o exemplo clássico de POO — uma conta bancária com regras de negócio reais, não só getters e setters simples.

Passos a realizar

  1. Cria conta-bancaria.js na pasta fase4.
  2. Cria uma classe ContaBancaria com constructor(titular, saldoInicial) que guarda titular, saldo, e um array vazio historico.
  3. Adiciona um método depositar(valor): se valor for menor ou igual a 0, imprime um erro e não faz nada; senão, soma ao saldo e adiciona uma string ao historico (ex: "Depósito de 50€").
  4. Adiciona um método levantar(valor): se valor for maior que o saldo disponível, imprime "Saldo insuficiente" e não faz nada; senão, subtrai do saldo e regista no historico.
  5. Adiciona um método extrato() que imprime o titular, o saldo atual, e cada linha do historico usando forEach.
  6. Cria duas instâncias de contas diferentes, faz várias operações em cada uma (incluindo pelo menos uma tentativa inválida em cada), e chama extrato() em ambas para confirmares que o estado de cada conta é independente.

Fluxo de funcionamento

  1. 1new ContaBancaria(titular, saldo)
  2. 2depositar / levantar valida regras
  3. 3Atualiza saldo e historico
  4. 4extrato() imprime tudo

Código de exemplo

conta-bancaria.js
class ContaBancaria {
  constructor(titular, saldoInicial) {
    this.titular = titular;
    this.saldo = saldoInicial;
    this.historico = [];
  }

  depositar(valor) {
    if (valor <= 0) {
      console.log("Valor de depósito inválido.");
      return;
    }
    this.saldo += valor;
    this.historico.push("Depósito de " + valor + "€");
  }

  levantar(valor) {
    // completa aqui: valida e atualiza this.saldo e this.historico
  }

  extrato() {
    // completa aqui: imprime titular, saldo, e cada linha do historico
  }
}

const contaDaAna = new ContaBancaria("Ana", 100);
contaDaAna.depositar(50);
contaDaAna.levantar(30);
contaDaAna.levantar(1000); // deve falhar
contaDaAna.extrato();
Ponto de partida — completa levantar() e extrato()

Dicas úteis

  • Cada instância criada com new tem o seu próprio saldo e historico — testa exatamente isso criando duas contas e confirmando que uma não afeta a outra.
  • Valida sempre a operação antes de alterar o saldo — nunca deixes o código subtrair primeiro e "descobrir" depois que o saldo ficou negativo.

Erros comuns

  • Esquecer this. antes de saldo ou historico dentro dos métodos, o que faz o JavaScript procurar uma variável solta que não existe.
  • Validar a operação depois de já teres alterado o saldo, em vez de antes — a ordem da validação importa.

As minhas dúvidas