Entendendo funções no Javascript
As funções no JavaScript são blocos de código reutilizáveis que executam uma ação específica e podem ser chamados sempre que necessário. Sem elas, seria praticamente impossível organizar uma aplicação Web de qualquer tamanho.
O JavaScript é a linguagem essencial do desenvolvimento Web e é muito versátil, pois roda tanto no navegador quanto no servidor. Além disso, todos os navegadores modernos, no desktop e no mobile, conseguem ler e interpretar a linguagem.
Porém, para que tudo isso funcione bem, precisamos aproveitar os melhores recursos que a linguagem oferece. Para mim, as funções são um recurso sem o qual não conseguimos viver no JavaScript.
Neste artigo, vamos entender o que são as funções, como escrever sua sintaxe, como usar parâmetros e valores padrão e, por fim, como chamá-las da melhor maneira no nosso código.
O que são funções no JavaScript
Função, no JavaScript e na maioria das linguagens de programação, é o nome que damos a um trecho distinto de código que executa uma determinada ação. Em outras palavras, ela agrupa instruções que rodam juntas a partir de uma simples chamada.
Podemos pensar em uma função como uma pequena máquina: ela recebe dados de entrada, processa esses dados e, opcionalmente, devolve um resultado. Segundo o guia de funções da MDN, as funções são um dos blocos fundamentais da linguagem.
Então, cada ação que a aplicação precisa executar pode virar uma função. Imagine que precisamos adicionar um nome: podemos criar uma função chamada addName() e colocar nela toda a lógica dessa ação.
function addName() {
// validar o nome
// incluir o nome
// retornar o nome
}
É muito importante criar funções distintas para cada ação. Nunca recomendo misturar responsabilidades ou criar uma única função que faça tudo na aplicação.
Quanto mais você dividir o código em pequenos trechos, melhor será o entendimento e a reutilização. Esse princípio também aparece nas boas práticas de clean code.
Por que usar funções no JavaScript
O primeiro benefício é a reutilização. Assim, você escreve a lógica uma única vez e a chama em vários pontos, em vez de copiar e colar o mesmo código.
O segundo benefício é a organização. Com funções no JavaScript, cada trecho ganha um nome que descreve o que ele faz, então o código fica mais fácil de ler.
O terceiro é a manutenção. Quando uma regra muda, você altera apenas a função responsável por ela, e todos os pontos que a chamam passam a usar o novo comportamento.
Por fim, as funções criam um escopo próprio. Dessa forma, as variáveis declaradas dentro delas não vazam para o resto da aplicação, o que reduz conflitos de nomes.
A sintaxe das funções no JavaScript
Como vimos, a função é um bloco de código que contém todas as instruções de uma tarefa. Podemos também aplicar o conceito de encapsulamento, em que cada bloco reúne apenas aquilo que faz sentido para aquela ação.
Entender a sintaxe das funções no JavaScript é simples, porque toda declaração segue a mesma fórmula: palavra reservada function, nome da função, parâmetros entre parênteses e corpo entre chaves.
Essa é a fórmula que vamos usar para criar todas as funções, mudando apenas o nome, os parâmetros e a lógica. Voltando à função de adicionar um nome, sua declaração fica assim:
function addName() {
}
Temos a palavra reservada function seguida do nome da função. Tudo o que fica dentro dos parênteses são parâmetros que podemos usar dentro da função.
Já as chaves fazem o papel do famoso begin e end. Ou seja, tudo o que estiver entre elas é o código que a função vai executar.
Parâmetros e argumentos
No caso da função anterior, não temos nenhum parâmetro. Mas imagine que precisamos receber um valor booleano para saber se o nome está ativo na base de dados.
Nesse caso, basta declarar esse parâmetro na assinatura da função.
function addName(isActive) {
}
Com o parâmetro isActive declarado, podemos usá-lo dentro da lógica da função.
function addName(isActive) {
if (isActive) {
// fluxo para nomes ativos
} else {
// fluxo para nomes inativos
}
}
Vale diferenciar dois termos. O parâmetro é o nome declarado na função, enquanto o argumento é o valor que você passa no momento da chamada.
Nas funções no JavaScript, a quantidade de argumentos não precisa bater com a de parâmetros. Se faltar um argumento, o parâmetro correspondente recebe undefined, e a função simplesmente ignora os argumentos extras.
Sempre que precisarmos de mais parâmetros, podemos adicioná-los dentro dos parênteses, separados por vírgula. Vamos imaginar um cenário diferente, em que precisamos somar dois números:
function sum(a, b) {
return a + b;
}
Vamos entender melhor o que cada parte quer dizer:
(a, b): são os parâmetros da função, neste caso o primeiro e o segundo número a serem somados.return: é a palavra reservada que devolve um valor para quem chamou a função.a + b: é a lógica da função, que podemos alterar conforme a necessidade.
A regra para criar uma função será sempre a mesma. Portanto, o que muda de uma função para outra é a lógica que cumpre a tarefa proposta.
O papel do return
O return encerra a execução da função e devolve o valor indicado. Por isso, o motor não executa nenhuma linha escrita depois dele, no mesmo fluxo.
Se a função não tiver um return, ela devolve undefined automaticamente. Isso acontece, por exemplo, em funções que apenas exibem algo no console.
function logMessage(message) {
console.log(message);
}
const result = logMessage("Hello");
console.log(result); // undefined
Function expression e arrow functions
Além de declarar funções da forma tradicional, também podemos criar funções como expressões. Essa forma se chama function expression e consiste em atribuir uma função a uma constante ou variável.
const sum = function (a, b) {
return a + b;
};
Essa função pode ou não ter nome. Quando não tem, ela recebe o nome de função anônima, algo muito comum em funções no JavaScript passadas para outras funções.
A partir do ECMAScript 2015, surgiu uma sintaxe ainda mais curta para function expressions: as arrow functions. Elas trocam a palavra function pelo símbolo =>.
Quando a função tem apenas uma expressão de retorno, podemos devolvê-la de forma direta, sem chaves e sem return. Vamos usar a mesma função de soma como exemplo:
const sum = (a, b) => a + b;
Nesse formato, a função retorna automaticamente o resultado da expressão depois do =>. Se precisar de várias linhas, basta usar chaves e escrever o return explicitamente.
const sumAndLog = (a, b) => {
const total = a + b;
console.log(total);
return total;
};
É uma forma muito prática de criar funções curtas, mas devemos ter cuidado com alguns pontos:
- Por mais prática que seja, a versão em uma linha pode dificultar a leitura para outros desenvolvedores quando a expressão fica longa.
- A depuração fica menos confortável, porque não há linhas separadas onde colocar um breakpoint.
- As arrow functions não têm seu próprio
thisnem o objetoarguments, e não podem ser usadas como construtoras comnew.
Caso esses pontos não sejam um problema, você pode usar esse recurso sempre que quiser. Na prática, arrow functions são muito comuns em callbacks, como em items.map((item) => item.name).
Callbacks e funções anônimas
Um callback é uma função que você entrega para outra função executar no momento certo. Por exemplo, o método setTimeout recebe uma função e a executa depois de um intervalo.
setTimeout(() => {
console.log("Executed after one second");
}, 1000);
Da mesma forma, os eventos do navegador usam callbacks. Quando o usuário clica em um botão, o navegador chama a função que você registrou para aquele evento.
const button = document.querySelector("#save");
button.addEventListener("click", () => {
console.log("Saved!");
});
Nesses dois exemplos, as funções no JavaScript são anônimas, porque só existem para aquele uso específico. No entanto, se a mesma lógica aparecer em vários lugares, vale extrair a função e dar um nome a ela.
Outro ponto importante é a remoção de eventos. Para usar removeEventListener, você precisa passar a mesma referência de função, então funções anônimas não servem nesse caso.
Hoisting nas funções no JavaScript
Existe uma diferença importante entre declarar uma função e criá-la como expressão. As declarações sofrem hoisting, ou seja, o motor as registra antes de executar o código.
Por isso, você pode chamar uma função declarada antes da linha em que ela aparece no arquivo.
console.log(square(5)); // 25
function square(n) {
return n * n;
}
Já as function expressions e as arrow functions só existem depois da atribuição. Então, chamar uma delas antes da linha de criação gera um erro.
// console.log(cube(3)); // ReferenceError com const
const cube = (n) => n * n * n;
Esse detalhe explica por que alguns times preferem declarar funções no JavaScript no topo do arquivo ou usar sempre expressões, para tornar a ordem de execução mais explícita.
Como utilizar o parâmetro default
É muito comum usarmos parâmetros que já têm um valor padrão. Já vimos que podemos usar ou não parâmetros, mas como definir um valor padrão para eles?
function sum(a = 5, b = 10) {
return a + b;
}
Repare que, diferentemente dos exemplos anteriores, agora o parâmetro a tem o valor padrão 5 e o parâmetro b tem o valor padrão 10.
Sempre que você chamar a função sem argumentos, a soma vai usar esses valores. Nesse caso, sum() devolve 15.
E, claro, quando precisarmos mudar esses valores, basta passar os argumentos na chamada da função.
function sum(a = 5, b = 10) {
return a + b;
}
sum(2, 2); // 4
sum(2); // 12, porque b usa o valor padrão
Dessa maneira, o valor passado na chamada substitui o valor padrão. Vale saber que o padrão também entra em ação quando você passa undefined explicitamente.
Rest parameters nas funções no JavaScript
Às vezes, não sabemos quantos argumentos a função vai receber. Para esse caso, as funções no JavaScript aceitam rest parameters, escritos com três pontos antes do nome.
function sumAll(...numbers) {
return numbers.reduce((total, current) => total + current, 0);
}
sumAll(1, 2, 3); // 6
sumAll(10, 20); // 30
O parâmetro numbers recebe todos os argumentos em um array. Com isso, podemos usar métodos como reduce, que você conhece melhor no guia básico sobre arrays no JavaScript.
Escopo das funções no JavaScript
Toda função cria um escopo. Assim, as variáveis declaradas dentro dela só existem ali e não podem ser acessadas de fora.
function calculateDiscount(price) {
const rate = 0.1;
return price * rate;
}
calculateDiscount(100); // 10
// console.log(rate); // ReferenceError: rate is not defined
Esse isolamento é uma vantagem, pois duas funções diferentes podem usar variáveis com o mesmo nome sem nenhum conflito. Além disso, a memória dessas variáveis fica livre quando ninguém mais as referencia.
Por outro lado, uma função interna consegue acessar as variáveis da função externa. Esse mecanismo se chama closure e mantém as variáveis vivas mesmo depois que a função externa termina.
function createCounter() {
let count = 0;
return () => {
count++;
return count;
};
}
const counter = createCounter();
counter(); // 1
counter(); // 2
Para entender melhor como let e const se comportam dentro desses escopos, leia o artigo sobre variáveis no JavaScript com var, let e const.
E afinal, como chamar uma função
Aqui precisamos compreender que declarar uma função é diferente de chamar uma função. Sempre que declaramos uma função, com parâmetros e lógica, ainda não estamos executando nada.
Para executar a função, devemos escrever o nome dela seguido dos parênteses com os argumentos. Vamos usar o exemplo anterior.
function sum(a = 5, b = 10) {
return a + b;
}
const total = sum(2, 2);
console.log(total); // 4
Se simplesmente ignorássemos a chamada sum(2, 2), a aplicação nunca executaria a função. Por isso, precisamos declarar a função e chamá-la sempre que for necessário.
Também vale notar a diferença entre sum e sum(). Sem parênteses, você apenas referencia a função, o que é útil para passá-la como argumento para outra função.
const numbers = [1, 2, 3];
function double(n) {
return n * 2;
}
// passamos a referência da função, sem executá-la
const doubled = numbers.map(double); // [2, 4, 6]
Esse recurso existe porque as funções no JavaScript são valores como quaisquer outros. Ou seja, podemos guardá-las em variáveis, passá-las como argumento e devolvê-las de outras funções.
Boas práticas para funções no JavaScript
Primeiro, dê nomes que comecem com verbos, como calculateTotal, sendEmail ou validateUser. Assim, quem lê sabe o que a função faz sem abrir o corpo dela.
Em seguida, mantenha cada função pequena e focada em uma única responsabilidade. Se ela começar a crescer demais, divida a lógica em funções menores.
Além disso, prefira poucos parâmetros. Quando a lista ficar longa, considere receber um único objeto com propriedades nomeadas.
Também documente o que cada função espera receber e devolver, principalmente em funções no JavaScript compartilhadas entre vários arquivos. Comentários JSDoc, por exemplo, ajudam o editor a mostrar dicas de parâmetros enquanto você digita.
/**
* Calcula o preço final com desconto.
* @param {number} price Preço original.
* @param {number} rate Percentual de desconto entre 0 e 1.
* @returns {number} Preço com desconto aplicado.
*/
function applyDiscount(price, rate = 0) {
return price - price * rate;
}
Por fim, evite alterar variáveis externas dentro da função sempre que possível. Funções que dependem só dos parâmetros e devolvem um resultado são mais fáceis de testar e de reutilizar.
O que podemos concluir
Neste artigo, vimos todo o básico necessário para declarar e usar funções no JavaScript. Também entendemos parâmetros, valores padrão, retorno, arrow functions, hoisting e escopo.
Como mencionado, a fórmula de criação e de chamada é a mesma em todos os arquivos JavaScript. O que muda é apenas a lógica que cada função deve executar.
Se você está começando agora, vale conhecer também o curso gratuito de JavaScript do balta.io e praticar esses conceitos com exercícios.
Eu espero que você tenha gostado desta leitura. Te vejo na próxima, bons códigos e até mais!
FAQ
Qual a diferença entre declaração de função e function expression?
A declaração usa function nome() e sofre hoisting. Já a function expression atribui a função a uma variável e só existe depois dessa atribuição.
Arrow function é a mesma coisa que function expression?
A arrow function é uma forma mais curta de function expression. No entanto, ela não tem this próprio nem pode ser usada com new.
O que uma função retorna se não tiver return?
Ela retorna undefined. Por isso, use return sempre que precisar devolver um resultado.
Quando o valor padrão de um parâmetro é usado?
Quando o argumento não é informado ou quando você passa undefined. Nos outros casos, o valor recebido substitui o padrão.
Posso passar uma função como argumento?
Sim, porque funções são valores no JavaScript. Basta passar o nome sem parênteses, como em numbers.map(double).