Funções em JavaScript: como criar, chamar e reaproveitar
Declaração, expressão, arrow function, hoisting, callback, parâmetro default e rest — com código executado no Node e os erros que cada um provoca.
Função é um pedaço de código com nome, que recebe valores, faz alguma coisa e devolve um resultado. Sempre que você percebe que copiou e colou três linhas para o terceiro lugar do projeto, era função que estava faltando.
Todos os exemplos aqui são de uma loja virtual: carrinho, frete, desconto. É o mesmo domínio que aparece em variáveis em JavaScript, a lição anterior desta trilha.
Os nomes corretos importam: parâmetro é a entrada prevista na definição, argumento é o valor usado numa chamada e retorno é o valor entregue ao resto do programa. Em palavras simples, a função é uma tarefa com um contrato de entrada e saída.
A máquina com entrada, trabalho e saída
Pense numa máquina de café: você escolhe tamanho e quantidade de açúcar, a máquina executa um processo e entrega a bebida. Os parâmetros são as escolhas disponíveis, os argumentos são os valores selecionados naquela vez e o retorno é o que saiu. A declaração monta a máquina; a chamada coloca uma entrada concreta para ela trabalhar.
No primeiro exemplo, anote parâmetros, argumentos e retorno antes de executar. Depois troque somente um argumento e preveja a nova saída. Se você explica o que mudou sem reler o corpo da função, o contrato ficou claro.
A forma mais direta é a declaração de função: a palavra function, um nome,
a lista de parâmetros e o corpo entre chaves.
function calcularSubtotal(preco, quantidade) {
return preco * quantidade;
}
console.log(calcularSubtotal(89.9, 3));
console.log(calcularSubtotal(19.5, 4));Repare no primeiro número. Não é bug da função: é como o JavaScript guarda
números com casas decimais, em ponto flutuante binário. Para dinheiro, formate
na hora de exibir (toFixed(2)) ou trabalhe em centavos, com inteiros.
preco e quantidade são os parâmetros — nomes que só existem dentro da
função. 89.9 e 3 são os argumentos — os valores reais da chamada.
Toda função devolve alguma coisa
Se você não escrever return, a função devolve undefined. Isso não é um
detalhe acadêmico: é a origem de metade dos bugs de iniciante.
function mostrarItem(nome, preco) {
console.log(`${nome} — R$ ${preco.toFixed(2)}`);
}
const retorno = mostrarItem('Teclado mecânico', 289.9);
console.log(retorno);A função imprimiu, mas não devolveu. Imprimir e devolver são coisas diferentes:
console.log fala com você, return fala com o resto do programa.
Declaração e expressão
Uma função também pode ser um valor guardado numa variável. Aí ela é uma expressão de função.
function aplicarDesconto(valor, percentual) {
return valor - valor * (percentual / 100);
}
const calcularFrete = function (cep, peso) {
return cep.startsWith('0') ? 12.5 : 24.9 + peso * 1.5;
};
console.log(aplicarDesconto(200, 15));
console.log(calcularFrete('01310100', 2));
console.log(calcularFrete('88010000', 2));As duas fazem a mesma coisa quando chamadas. A diferença aparece antes da linha em que foram escritas.
Hoisting: por que uma funciona antes e a outra não
Declarações de função são içadas inteiras para o topo do escopo. Você pode chamá-las acima da definição:
console.log(calcularSubtotal(49.9, 2));
function calcularSubtotal(preco, quantidade) {
return preco * quantidade;
}Com uma expressão guardada em const, a mesma ordem quebra. O nome existe, mas
está na zona morta temporal (TDZ) até a linha da atribuição:
console.log(calcularFrete('01310100'));
const calcularFrete = (cep) => (cep.startsWith('0') ? 12.5 : 24.9);Trocando const por var, o erro muda de nome — e fica pior de diagnosticar,
porque agora a variável existe valendo undefined:
console.log(calcularFrete('01310100'));
var calcularFrete = function (cep) {
return cep.startsWith('0') ? 12.5 : 24.9;
};Arrow function
A arrow function é uma sintaxe mais curta para expressões de função. Com um só
parâmetro e um só return, ela cabe numa linha:
const precoComImposto = (preco) => preco * 1.18;
console.log(precoComImposto(100));
console.log([19.9, 49.9, 129.9].map(precoComImposto));Sem chaves, o valor da expressão já é o retorno. Se você abrir chaves, precisa
escrever return de novo.
Arrow function não tem this próprio
Esta é a diferença que importa, e a que mais derruba gente em entrevista. Uma
arrow herda o this de onde foi escrita; uma função normal recebe o this
de quem a chamou.
const carrinho = {
cliente: 'Ana',
itens: ['Teclado', 'Mouse'],
resumo() {
return `${this.cliente} tem ${this.itens.length} itens`;
},
resumoComArrow: () => `${this.cliente} tem ${this.itens.length} itens`,
};
console.log(carrinho.resumo());
console.log(carrinho.resumoComArrow());O método normal funcionou. A arrow não: no topo de um módulo, this é
undefined, e é esse undefined que ela herdou. Regra prática: método de
objeto nunca é arrow.
Callback: função passada como argumento
Como função é um valor, ela pode viajar como argumento. Quem recebe decide quando chamar.
const itens = [
{ nome: 'Teclado mecânico', preco: 289.9, categoria: 'periferico' },
{ nome: 'Cadeira gamer', preco: 1199, categoria: 'movel' },
{ nome: 'Mouse sem fio', preco: 149.9, categoria: 'periferico' },
];
function totalDe(itens, filtro) {
return itens.filter(filtro).reduce((soma, item) => soma + item.preco, 0);
}
console.log(totalDe(itens, (item) => item.categoria === 'periferico'));
console.log(totalDe(itens, (item) => item.preco > 500));totalDe não sabe nada sobre categoria nem sobre preço mínimo. Ela sabe somar;
a regra vem de fora. É assim que map, filter e reduce funcionam por
dentro.
Parâmetro default
Um parâmetro pode ter valor de reserva, usado quando o argumento não vem:
function calcularFrete(peso, regiao = 'sudeste') {
const tabela = { sudeste: 12.5, sul: 18.9, nordeste: 27.4, norte: 34.9 };
return (tabela[regiao] ?? 39.9) + peso * 1.5;
}
console.log(calcularFrete(2));
console.log(calcularFrete(2, 'nordeste'));
console.log(calcularFrete(2, 'antartida'));O default entra só para undefined. null, 0 e string vazia são valores
legítimos e passam direto:
function aplicarCupom(valor, desconto = 10) {
return valor - desconto;
}
console.log(aplicarCupom(100));
console.log(aplicarCupom(100, undefined));
console.log(aplicarCupom(100, null));
console.log(aplicarCupom(100, 0));null virou zero na subtração. Se o valor pode vir de um formulário ou de uma
API, trate essa possibilidade — o default não te protege dela.
Rest: quantidade variável de argumentos
Três pontos antes do último parâmetro juntam tudo que sobrou num array:
function resumirCarrinho(cliente, ...precos) {
const total = precos.reduce((soma, preco) => soma + preco, 0);
return `${cliente}: ${precos.length} itens, R$ ${total.toFixed(2)}`;
}
console.log(resumirCarrinho('Ana', 289.9, 149.9, 39.9));
console.log(resumirCarrinho('Bruno'));Sem nenhum argumento extra, precos é um array vazio — nunca undefined. Isso
faz o reduce funcionar sem verificação nenhuma.
Quando extrair uma função
Iniciante costuma perguntar quantas linhas uma função deve ter. A pergunta é
outra: a função tem um nome honesto? Se você consegue nomear o trecho sem
usar “e” no meio — calcularFrete, não calcularFreteEAplicarCupom — ele é uma
função. Se não consegue, provavelmente são duas.
Três sinais de que chegou a hora de extrair:
- O mesmo trecho aparece pela segunda vez. Não espere a terceira.
- Você precisou escrever um comentário explicando o que as próximas cinco linhas fazem. Esse comentário é o nome da função.
- A função tem mais de um nível de
ifdentro defordentro deif. Cada nível que vira função separada devolve legibilidade.
E o contrário também vale: função de uma linha chamada de um lugar só, com nome pior que o próprio código, é ruído. Extrair não é virtude por si.
O erro mais comum: x is not a function
Você definiu o método com um nome e chamou com outro:
const carrinho = {
itens: [],
adicionar(item) {
this.itens.push(item);
},
};
carrinho.adicionarItem({ nome: 'Teclado mecânico', preco: 289.9 });O erro cita o caminho completo — carrinho.adicionarItem. Quando o nome antes
do ponto está certo e o depois do ponto não existe, é typo ou é método de outra
versão da API. console.log(Object.keys(carrinho)) resolve em dez segundos.
Quando o problema é o contrário, e o objeto inteiro é que não existe, a mensagem muda para TypeError: Cannot read properties of undefined.
O que vem depois
Com funções na mão, o próximo passo da trilha de JavaScript são arrays e objetos — e depois o código assíncrono, onde as funções passam a ser chamadas por outra pessoa, em outro momento. Se quiser ver o mapa inteiro antes de continuar, o guia completo de JavaScript mostra a ordem de estudo e onde cada assunto entra.
Prefere aprender em vídeo?
Tem uma aula sobre este assunto no nosso canal.
Perguntas frequentes
Qual a diferença entre função e método?
Devo usar arrow function em tudo?
Função pode devolver outra função?
Quantos parâmetros uma função deveria ter?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — Functions — developer.mozilla.org
- ECMAScript 2026 Language Specification — Function Definitions — tc39.es



