3. Funções#
Em JavaScript, funções vão além de simples blocos de código reutilizável. Elas são valores: podem ser armazenadas em variáveis, passadas como argumento para outras funções e retornadas como resultado. Por isso, diz-se que funções são cidadãs de primeira classe na linguagem — elas têm os mesmos “direitos” que números, strings ou qualquer outro valor.
Essa característica tem consequências práticas importantes. Ela é o que torna possível trabalhar com callbacks, entender como métodos funcionam dentro de classes e perceber que o encapsulamento — a ideia de esconder detalhes internos e expor apenas o necessário — começa muito antes da orientação a objetos.
O que é uma função?#
Uma função é um bloco de código com um propósito definido. Ela pode receber dados de entrada (os parâmetros), executar uma sequência de instruções e devolver um resultado (o retorno). Mas não precisa fazer tudo isso; uma função pode não receber nada, não retornar nada, ou ambos.
function saudacao() {
console.log("Olá!");
}Para executar a função, o que chamamos de “invocar uma função, basta usar parênteses.
saudacao();Formas de declarar funções#
Declaração de função#
A forma mais tradicional. Tem nome, pode usar parâmetros e retorna um valor com return.
function soma(a, b) {
return a + b;
}
let resultado = soma(2, 3);
console.log(resultado); // 5
Uma característica particular da declaração de função é o hoisting. Não se espante com o nome complexo, ele significa que o JavaScript lê as declarações antes de executar o código, então é possível chamar uma função antes mesmo de ela aparecer no arquivo. Na prática, isso raramente importa, mas explica por que a ordem das declarações é mais flexível do que parece.
Função anônima#
Uma função sem nome, armazenada em uma variável. Quem dá identidade a ela é a variável.
let saudacao = function () {
console.log("Olá!");
};
saudacao();Função seta (arrow function)#
Uma sintaxe mais concisa, introduzida no ES6 (ECMAScript 6 - a versão 2015 de JavaScript que trouxe melhorias significativas para a sintaxe da linguagem). É muito usada para funções curtas e para callbacks.
let soma = (a, b) => {
return a + b;
};Quando o corpo da função é uma única expressão, o return pode ser omitido:
let soma = (a, b) => a + b;Se houver apenas um parâmetro, os parênteses também são opcionais:
let dobro = (x) => x * 2;As funções seta têm uma diferença importante em relação às funções tradicionais no comportamento da palavra-chave
this. Isso ficará evidente quando chegar o momento de estudar orientação a objetos — por ora, vale apenas saber que elas não substituem as declarações ja apresentadas em todos os contextos.
Parâmetros e retorno#
Parâmetros são as variáveis que a função espera receber. Eles funcionam como variáveis locais, definidas no momento da chamada.
function dobro(numero) {
return numero \* 2;
}
dobro(4); // 8
Se a função for chamada sem o argumento esperado, o parâmetro recebe undefined:
dobro(); // NaN — porque undefined * 2 é NaN
O return encerra a execução da função imediatamente e define o valor que ela devolve. Uma função pode ter múltiplos return, mas apenas um será executado por chamada, geralmente em diferentes ramos de uma condição:
function verificaIdade(idade) {
if (idade >= 18) {
return "Maior de idade";
}
return "Menor de idade";
}Quando uma função não tem return, ela retorna undefined por padrão.
function imprime(valor) {
console.log(valor);
}
let x = imprime(10); // 10 é impresso no console
console.log(x); // undefined, porque estamos imprimindo o retorno de imprime(), guardado em x
Escopo de função#
Variáveis declaradas dentro de uma função só existem dentro dela. Elas nascem quando a função começa a executar e desaparecem quando ela termina.
function exemplo() {
let x = 10;
console.log(x);
}
exemplo();
console.log(x); // Erro: x não está definido fora
Isso é encapsulamento na sua forma mais direta: os detalhes internos da função não vazam para o escopo externo. Quem chama a função não precisa saber como ela funciona por dentro. Precisa saber apenas o que ela recebe e o que ela devolve.
Funções como valores#
Funções sendo “cidadãs de primeira classe” significa, na prática, que podem ser manipuladas como qualquer outro valor: serem atribuídas a uma variável, passadas como argumento ou até retornadas por outra função.
Função como argumento (callback)
function executar(funcao) {
funcao();
}
function mensagem() {
console.log("Executando função");
}
executar(mensagem);Note que mensagem é passada sem parênteses — ela não é executada no momento da passagem. Quem decide quando chamá-la é a função executar. Essa ideia de “passar uma função para ser chamada mais tarde” é o que chamamos de callback.
Um exemplo mais concreto:
function processar(valor, operacao) {
return operacao(valor);
}
let dobro = (x) => x * 2;
let triplo = (x) => x * 3;
processar(5, dobro); // 10
processar(5, triplo); // 15
A função processar não sabe (e não precisa saber) o que operacao faz. Ela apenas a chama com o valor recebido. Isso torna o código flexível: o comportamento muda dependendo de qual função é passada.
Funções retornando funções#
Uma função também pode produzir outra função como resultado:
function criarMultiplicador(fator) {
return function (numero) {
return numero * fator;
};
}
let duplicar = criarMultiplicador(2);
let triplicar = criarMultiplicador(3);
duplicar(5); // 10
triplicar(5); // 15
A função interna “lembra” o valor de fator mesmo depois de criarMultiplicador ter terminado de executar. Esse comportamento tem nome: closure. É um mecanismo poderoso que reaparecerá ao longo da disciplina.
Atividades sugeridas#
(Questão 01) Acompanhe o fluxo do código abaixo passo a passo e determine o valor final impresso.
function teste(x) {
return x \* 2;
}
let y = teste(3);
console.log(y);Qual é o valor final de y?
(Questão 02) Analise o código a seguir.
function imprime(valor) {
console.log(valor);
}
let resultado = imprime(10);
console.log(resultado);a. O que será impresso?
b. Qual é o valor de resultado?
(Questão 03) Analise o código a seguir.
function executar(a, b, operacao) {
return operacao(a, b);
}
let soma = (x, y) => x + y;
let sub = (x, y) => x - y;
executar(10, 5, soma);
executar(10, 5, sub);a. Quais são os resultados de cada chamada?
b. Por que o parâmetro operacao precisa ser uma função e não um número ou string?
(Questão 04) Reescreva a função abaixo como função seta:
function quadrado(x) {
return x * x;
}Depois, reescreva novamente na forma mais compacta possível.
(Questão 05) Escreva uma função chamada aplicar que receba um número e uma função, e retorne o resultado de aplicar essa função ao número. Em seguida, use aplicar para:
- calcular o dobro de 7;
- calcular a raiz quadrada de 49 (use
Math.sqrt).
(Questão 06) Escreva uma função criarSomador(n) que retorne outra função. A função retornada deve receber um número e somar n a ele.
Exemplo de uso esperado:
let soma5 = criarSomador(5);
soma5(3); // 8
soma5(10); // 15
(Questão 07) Responda com suas palavras:
a. O que significa dizer que funções são “cidadãs de primeira classe” em JavaScript?
b. Qual é a diferença prática entre passar mensagem e passar mensagem() como argumento para outra função?
c. O escopo de função é uma forma de encapsulamento? Justifique.