Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

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.

Rodolfo Mori6 min de leitura

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.

js
function calcularSubtotal(preco, quantidade) {
  return preco * quantidade;
}

console.log(calcularSubtotal(89.9, 3));
console.log(calcularSubtotal(19.5, 4));
269.70000000000005 78

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.

js
function mostrarItem(nome, preco) {
  console.log(`${nome} — R$ ${preco.toFixed(2)}`);
}

const retorno = mostrarItem('Teclado mecânico', 289.9);
console.log(retorno);
Teclado mecânico — R$ 289.90 undefined

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.

js
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));
170 12.5 27.9

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:

js
console.log(calcularSubtotal(49.9, 2));

function calcularSubtotal(preco, quantidade) {
  return preco * quantidade;
}
99.8

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:

js
console.log(calcularFrete('01310100'));

const calcularFrete = (cep) => (cep.startsWith('0') ? 12.5 : 24.9);
file:///private/tmp/loja/frete.mjs:1 console.log(calcularFrete('01310100')); ^ ReferenceError: Cannot access 'calcularFrete' before initialization at file:///private/tmp/loja/frete.mjs:1:13 at ModuleJob.run (node:internal/modules/esm/module_job:439:25) Node.js v24.16.0

Trocando const por var, o erro muda de nome — e fica pior de diagnosticar, porque agora a variável existe valendo undefined:

js
console.log(calcularFrete('01310100'));

var calcularFrete = function (cep) {
  return cep.startsWith('0') ? 12.5 : 24.9;
};
file:///private/tmp/loja/frete-var.mjs:1 console.log(calcularFrete('01310100')); ^ TypeError: calcularFrete is not a function at file:///private/tmp/loja/frete-var.mjs:1:13 Node.js v24.16.0

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:

js
const precoComImposto = (preco) => preco * 1.18;

console.log(precoComImposto(100));
console.log([19.9, 49.9, 129.9].map(precoComImposto));
118 [ 23.481999999999996, 58.882, 153.282 ]

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.

js
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());
Ana tem 2 itens file:///private/tmp/loja/carrinho-this.mjs:9 resumoComArrow: () => `${this.cliente} tem ${this.itens.length} itens`, ^ TypeError: Cannot read properties of undefined (reading 'cliente') at Object.resumoComArrow (file:///private/tmp/loja/carrinho-this.mjs:9:33) Node.js v24.16.0

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.

js
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));
439.79999999999995 1199

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:

js
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'));
15.5 30.4 42.9

O default entra para undefined. null, 0 e string vazia são valores legítimos e passam direto:

js
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));
90 90 100 100

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:

js
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'));
Ana: 3 itens, R$ 479.70 Bruno: 0 itens, R$ 0.00

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 if dentro de for dentro de if. 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:

js
const carrinho = {
  itens: [],
  adicionar(item) {
    this.itens.push(item);
  },
};

carrinho.adicionarItem({ nome: 'Teclado mecânico', preco: 289.9 });
file:///private/tmp/loja/carrinho.mjs:8 carrinho.adicionarItem({ nome: 'Teclado mecânico', preco: 289.9 }); ^ TypeError: carrinho.adicionarItem is not a function at file:///private/tmp/loja/carrinho.mjs:8:10 Node.js v24.16.0

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.

Ver todos os vídeos do canal
  • funcoes
  • hoisting
  • arrow function
  • callback
  • this

Perguntas frequentes

Qual a diferença entre função e método?
Método é o nome que damos a uma função quando ela está guardada como propriedade de um objeto. A mecânica é a mesma; muda só o acesso ao this, que passa a apontar para o objeto que fez a chamada.
Devo usar arrow function em tudo?
Não. Arrow é excelente para callbacks curtos e para funções que precisam herdar o this de fora. Para métodos de objeto e para funções que você quer chamar antes da linha em que aparecem, use a declaração function.
Função pode devolver outra função?
Pode, e isso é a base de padrões como fábrica de configuração e middleware. Uma função que devolve função guarda as variáveis do escopo onde nasceu — é o que se chama de closure.
Quantos parâmetros uma função deveria ter?
Na prática, até três. Passando disso, troque a lista de parâmetros por um único objeto com nomes. Fica mais legível na chamada e você para de depender da ordem.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

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

  1. MDN — Functions — developer.mozilla.org
  2. ECMAScript 2026 Language Specification — Function Definitions — tc39.es

Continue por aqui