Funções: declaração, expressão e arrow functions

[5] Funções: declaração, expressão e arrow functions

Uma função sem return devolve undefined — e uma arrow com chaves, mas sem return, devolve o mesmo silêncio. O artigo separa declaração, expressão e arrow, mostra por que só a primeira pode ser chamada antes de existir, e chega ao callback: passar a função pelo nome, nunca o resultado dela.
Javascript

12 min de leitura

Se os laços evitam a repetição de ações, as funções evitam a repetição de código. Elas são o bloco de construção mais importante de qualquer programa JavaScript — e de qualquer linguagem de programação moderna.

Uma função é um trecho de código com nome próprio que você pode chamar quantas vezes quiser, de qualquer lugar do programa. Sem funções, seu código seria uma lista gigante de instruções impossível de manter.

Por que funções existem?

Antes de ver a sintaxe, entenda o problema que elas resolvem:

// Sem funções — repetição de lógica:
const area1 = 5 * 3;
console.log(`Área: ${area1}`);

const area2 = 8 * 4;
console.log(`Área: ${area2}`);

const area3 = 12 * 7;
console.log(`Área: ${area3}`);

// Com função — lógica definida uma vez, usada várias:
function calcularArea(largura, altura) {
  return largura * altura;
}

console.log(`Área: ${calcularArea(5, 3)}`);
console.log(`Área: ${calcularArea(8, 4)}`);
console.log(`Área: ${calcularArea(12, 7)}`);

Muito melhor. Se a lógica mudar, você altera em um único lugar.

Declaração de função (Function Declaration)

A forma mais clássica de criar uma função:

function saudar(nome) {
  console.log(`Olá, ${nome}! Bem-vindo ao curso.`);
}

saudar("Ana");    // Olá, Ana! Bem-vindo ao curso.
saudar("Carlos"); // Olá, Carlos! Bem-vindo ao curso.
  • function é a palavra-chave
  • saudar é o nome da função
  • nome é o parâmetro — uma variável local que recebe o valor passado
  • "Ana" é o argumento — o valor real passado na chamada

Parâmetros e retorno

Uma função pode receber múltiplos parâmetros e retornar um valor com return:

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

const resultado = somar(10, 5);
console.log(resultado); // 15

// O return encerra a função imediatamente
function verificarIdade(idade) {
  if (idade < 18) {
    return "Menor de idade.";
  }
  return "Maior de idade.";
}

console.log(verificarIdade(15)); // Menor de idade.
console.log(verificarIdade(22)); // Maior de idade.

Quando uma função não tem return, ela retorna undefined automaticamente.

Parâmetros padrão (default parameters)

Você pode definir valores padrão para parâmetros caso nenhum argumento seja passado:

function cumprimentar(nome = "visitante", periodo = "dia") {
  return `Bom ${periodo}, ${nome}!`;
}

console.log(cumprimentar("Maria", "tarde")); // Bom tarde, Maria!
console.log(cumprimentar("João"));           // Bom dia, João!
console.log(cumprimentar());                 // Bom dia, visitante!

Extremamente útil para criar funções flexíveis sem quebrar o código quando um argumento é omitido.

Expressão de função (Function Expression)

Em JavaScript, funções são cidadãos de primeira classe — isso significa que podem ser atribuídas a variáveis, passadas como argumento e retornadas por outras funções.

const multiplicar = function(a, b) {
  return a * b;
};

console.log(multiplicar(4, 3)); // 12

A diferença prática em relação à declaração: expressões não sofrem hoisting — você não pode chamá-las antes de defini-las no código. Falaremos mais sobre hoisting no artigo Escopo, Hoisting e Closures.

Arrow Functions — a sintaxe moderna

Introduzida no ES6 (2015), a arrow function é uma forma mais concisa de escrever funções. É a forma que você mais vai ver em código moderno, React, e Node.js:

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

// Arrow function equivalente:
const dobrar = (n) => {
  return n * 2;
};

// Versão ainda mais curta (quando há apenas um return):
const dobrar = (n) => n * 2;

console.log(dobrar(7)); // 14

Quando a função tem apenas um parâmetro, os parênteses são opcionais:

const quadrado = n => n * n;
console.log(quadrado(5)); // 25

Quando não tem parâmetros, os parênteses são obrigatórios:

const dizerOla = () => "Olá!";
console.log(dizerOla()); // Olá!

Comparativo das três formas

// 1. Declaração de função
function somar(a, b) {
  return a + b;
}

// 2. Expressão de função
const somar = function(a, b) {
  return a + b;
};

// 3. Arrow function
const somar = (a, b) => a + b;

// As três fazem a mesma coisa neste caso:
console.log(somar(3, 4)); // 7

Funções dentro de funções

Funções podem chamar outras funções — e é assim que construímos programas complexos a partir de peças simples:

function calcularDesconto(preco, percentual) {
  return preco * (percentual / 100);
}

function calcularPrecoFinal(preco, percentual) {
  const desconto = calcularDesconto(preco, percentual);
  return preco - desconto;
}

const precoFinal = calcularPrecoFinal(200, 15);
console.log(`Preço final: R$ ${precoFinal}`); // Preço final: R$ 170

Cada função faz uma coisa só e faz bem feito. Isso é o começo do que chamamos de clean code — código limpo e fácil de manter.

Funções como argumentos (callbacks)

Uma das características mais poderosas do JavaScript: passar funções como argumento para outras funções. Esse conceito se chama callback e vai aparecer muito nos próximos artigos.

function executarOperacao(a, b, operacao) {
  return operacao(a, b);
}

const soma = (a, b) => a + b;
const subtracao = (a, b) => a - b;
const multiplicacao = (a, b) => a * b;

console.log(executarOperacao(10, 5, soma));          // 15
console.log(executarOperacao(10, 5, subtracao));     // 5
console.log(executarOperacao(10, 5, multiplicacao)); // 50

Você passa a função como se fosse um dado. O JavaScript executa ela no momento certo. Elegante e poderoso.

Boas práticas com funções

1. Nomes descritivos — verbos que explicam a ação:

// ❌ Ruim — nome vago
function calc(x, y) { return x + y; }

// ✅ Bom — nome claro
function calcularTotalPedido(subtotal, frete) {
  return subtotal + frete;
}

2. Uma função, uma responsabilidade:

// ❌ Ruim — faz coisas demais
function processarPedido(pedido) {
  // valida o pedido
  // calcula o total
  // aplica desconto
  // envia email
  // salva no banco
}

// ✅ Bom — cada função tem seu papel
function validarPedido(pedido) { /* ... */ }
function calcularTotal(pedido) { /* ... */ }
function aplicarDesconto(total, cupom) { /* ... */ }

3. Prefira retornar valores a usar efeitos colaterais:

// ❌ Evite modificar variáveis externas dentro da função
let total = 0;
function somar(valor) {
  total += valor; // efeito colateral — difícil de rastrear
}

// ✅ Prefira receber e retornar
function somar(totalAtual, valor) {
  return totalAtual + valor;
}

Tarefa para você

Desafio 1: Crie uma função calcularIMC(peso, altura) que retorne o IMC e uma mensagem de classificação.

// IMC = peso / (altura * altura)
// < 18.5 → Abaixo do peso
// 18.5 a 24.9 → Peso normal
// 25 a 29.9 → Sobrepeso
// >= 30 → Obesidade

console.log(calcularIMC(70, 1.75)); // deve retornar algo como "IMC: 22.9 — Peso normal"

Desafio 2: Reescreva a função acima como arrow function de uma linha (só o cálculo, sem a mensagem).

const calcularIMC = (peso, altura) => ???;
Ver solução — calcularIMC com classificação, e a versão em uma linha
// ---------------------------------------------------------------
// Desafio 1 — IMC com mensagem de classificação
// ---------------------------------------------------------------
function calcularIMC(peso, altura) {
  // Validar antes de calcular: altura 0 daria Infinity, e "Infinity —
  // Obesidade" é pior que um erro claro.
  if (typeof peso !== "number" || typeof altura !== "number") {
    throw new TypeError("peso e altura precisam ser números");
  }
  if (peso <= 0 || altura <= 0) {
    throw new RangeError("peso e altura precisam ser maiores que zero");
  }

  const imc = peso / (altura * altura);

  // As faixas oficiais não têm buraco entre 24.9 e 25: quem tem IMC 24.95
  // precisa cair em algum lugar. Por isso o corte é < 25, não <= 24.9.
  let classificacao;
  if (imc < 18.5) {
    classificacao = "Abaixo do peso";
  } else if (imc < 25) {
    classificacao = "Peso normal";
  } else if (imc < 30) {
    classificacao = "Sobrepeso";
  } else {
    classificacao = "Obesidade";
  }

  return `IMC: ${imc.toFixed(1)} — ${classificacao}`;
}

console.log(calcularIMC(70, 1.75));  // IMC: 22.9 — Peso normal
console.log(calcularIMC(95, 1.75));  // IMC: 31.0 — Obesidade
console.log(calcularIMC(77, 1.75));  // IMC: 25.1 — Sobrepeso

// O caso limite, que parece bug e não é: 76.5 kg dá IMC 24.979...,
// exibido como "25.0" pelo toFixed(1) — mas a classificação olha o
// valor cheio, que ainda é menor que 25.
console.log(calcularIMC(76.5, 1.75)); // IMC: 25.0 — Peso normal

// Arredondar para EXIBIR e arredondar para DECIDIR são coisas
// diferentes. Se a regra de negócio for a faixa que aparece na tela,
// arredonde antes de classificar — e uma vez só:
const imcExibido = Number((76.5 / (1.75 * 1.75)).toFixed(1));
console.log(imcExibido, imcExibido >= 25); // 25 true

// ---------------------------------------------------------------
// Desafio 2 — só o cálculo, em uma linha
// ---------------------------------------------------------------
// Sem chaves, a arrow retorna a expressão direto: não escreva `return`.
const imcDe = (peso, altura) => peso / (altura * altura);

console.log(imcDe(70, 1.75)); // 22.857142857142858

// Com chaves ela vira corpo de bloco e precisa de return explícito.
// Este é o erro mais comum de quem está começando:
const imcErrado = (peso, altura) => {
  peso / (altura * altura);
};
console.log(imcErrado(70, 1.75)); // undefined — faltou o return

// ---------------------------------------------------------------
// Devolver objeto em vez de frase pronta
// ---------------------------------------------------------------
// Frase pronta serve para exibir e mais nada. Devolvendo dados, quem
// chamou decide se vai exibir, comparar ou somar.
const classificar = (imc) =>
  imc < 18.5 ? "Abaixo do peso"
  : imc < 25 ? "Peso normal"
  : imc < 30 ? "Sobrepeso"
  : "Obesidade";

function avaliarIMC(peso, altura) {
  const imc = imcDe(peso, altura);
  return { imc: Number(imc.toFixed(1)), classificacao: classificar(imc) };
}

const { imc, classificacao } = avaliarIMC(70, 1.75);
console.log(imc, classificacao); // 22.9 'Peso normal'

// Para retornar um OBJETO literal em arrow de uma linha, envolva em
// parênteses — sem eles, `{` é lido como início de bloco:
const par = (peso, altura) => ({ peso, altura });
console.log(par(70, 1.75)); // { peso: 70, altura: 1.75 }

toFixed() devolve string, não número: imc.toFixed(1) + 1 resulta em "22.91". Pior que isso é decidir sobre o valor cheio e mostrar o arredondado — a tela exibe 25.0 e o sistema classifica como "Peso normal", e ninguém entende por quê. Arredonde uma vez, com Number(), e use o mesmo número para as duas coisas.

Uma função é, antes de tudo, um nome dado a um pedaço de comportamento — e é por isso que ela organiza o código mesmo quando é chamada uma vez só. A escolha entre declaração, expressão e arrow raramente muda o resultado: muda a partir de quando o nome existe e o que acontece com o this. Já passar função como argumento, que aqui apareceu com o nome de callback, é o mecanismo sobre o qual quase tudo que vem adiante é construído.

Fontes e Referências

Exercícios

Exercício 1

O que as duas linhas finais imprimem?

function saudar(nome) {
  console.log(`Olá, ${nome}!`);
}

const retorno = saudar("Ana");
console.log(retorno);
Ver resposta

✓ Resposta: Sai Olá, Ana! e depois undefined. A função imprime, mas não retorna — e toda função sem return devolve undefined. A distinção é essencial: console.log mostra na tela e acabou; return entrega um valor de volta a quem chamou, que pode guardá-lo, somá-lo, passá-lo adiante. Confundir os dois é o motivo mais comum de uma função "que funciona" produzir undefined quando alguém tenta usar o resultado.

Exercício 2

Anote as três saídas. A primeira revela um defeito da função — qual é, e como você o corrigiria?

function cumprimentar(nome = "visitante", periodo = "dia") {
  return `Bom ${periodo}, ${nome}!`;
}

console.log(cumprimentar("Maria", "tarde"));
console.log(cumprimentar("João"));
console.log(cumprimentar());
Ver resposta

✓ Resposta: Sai Bom tarde, Maria!, Bom dia, João! e Bom dia, visitante!. O defeito está na primeira: em português, é "Boa tarde" e "Boa noite" — o gênero do artigo muda com o período, e a função presume que "Bom" serve para todos. É um erro de montar frase por concatenação, comum em qualquer idioma com concordância. A correção honesta não é remendar o template, e sim guardar a saudação inteira: function cumprimentar(nome = "visitante", saudacao = "Bom dia") { return `${saudacao}, ${nome}!`; }.

Exercício 3

As duas chamadas abaixo acontecem antes das definições. Uma funciona e a outra não. Qual é qual, e que erro aparece?

console.log(somar(2, 3));
console.log(multiplicar(2, 3));

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

const multiplicar = function (a, b) {
  return a * b;
};
Ver resposta

✓ Resposta: A primeira funciona e imprime 5; a segunda lança ReferenceError: Cannot access 'multiplicar' before initialization — e, por ser um erro, nem chega a imprimir. A declaração de função é içada inteira para o topo do escopo (o hoisting), então somar já existe antes da linha em que foi escrita. A expressão de função é apenas um valor atribuído a uma const, e uma const não pode ser lida antes da linha que a inicializa.

Exercício 4

Três tentativas de escrever a mesma arrow function. Quais delas imprimem 7, e o que a outra imprime?

const somarA = (a, b) => a + b;
const somarB = (a, b) => { return a + b; };
const somarC = (a, b) => { a + b; };

console.log(somarA(3, 4));
console.log(somarB(3, 4));
console.log(somarC(3, 4));
Ver resposta

✓ Resposta: somarA e somarB imprimem 7; somarC imprime undefined. A arrow tem duas formas: sem chaves, o corpo é uma expressão e seu valor é retornado automaticamente; com chaves, o corpo é um bloco comum e o return passa a ser obrigatório. Em somarC, a + b é calculado e descartado — a função chega ao fim sem retornar nada. É o deslize clássico de quem acrescenta chaves a uma arrow curta e esquece de acrescentar também o return.

Exercício 5

Alguém trocou soma por soma(10, 5) na chamada. O que acontece?

function executarOperacao(a, b, operacao) {
  return operacao(a, b);
}

const soma = (a, b) => a + b;

console.log(executarOperacao(10, 5, soma(10, 5)));
Ver resposta

✓ Resposta: Lança TypeError: operacao is not a function. Com os parênteses, soma(10, 5) é executado na hora e o que chega ao parâmetro operacao é o número 15 — e números não podem ser chamados. Um callback se passa pelo nome, sem parênteses: os parênteses são o que executa. É a mesma distinção entre entregar a receita e entregar o bolo pronto; executarOperacao quer a receita, para decidir ela mesma quando cozinhar.

Comentários

Mais em Javascript

Projeto Final: Revisão Completa e Aplicação de Produção
Projeto Final: Revisão Completa e Aplicação de Produção

O fim da série reúne tudo numa aplicação de produção: React com rotas e…

Fetch API: consumindo dados da internet
Fetch API: consumindo dados da internet

Aqui os dados deixam de ser simulados com setTimeout e passam a vir da…

MongoDB e Mongoose: banco de dados com Node
MongoDB e Mongoose: banco de dados com Node

Um array na memória some quando o servidor reinicia, e é aí que entra o banco…