Identificação
- Duração: 2 horas
- Tipo: teoria aplicada e laboratório
- Entrega: biblioteca de regras para tarefas
- Laboratório:
../exemplos/aula-2.3/index.html
Introdução
O que é uma função?
Uma função é um bloco de código que recebe um nome e pode ser executado quando necessário. Ela serve para reunir uma responsabilidade e evitar a repetição da mesma regra em vários lugares.
function greetStudent() {
return "Olá, estudante!";
}
const message = greetStudent();
Neste exemplo:
greetStudenté o nome da função;()executa, ou chama, a função;returnentrega o resultado;messagerecebe o texto retornado.
Uma função não é executada apenas por existir. Escrever greetStudent referencia a
função; escrever greetStudent() chama a função.
Por que não copiar e colar o mesmo código?
Imagine que a regra de prioridade apareça no formulário, no cartão da tarefa e no relatório. Se copiarmos a regra três vezes, uma alteração futura pode atualizar apenas duas cópias. O sistema passará a fornecer respostas diferentes.
Podemos concentrar a regra:
function classifyPriority(isUrgent, isImportant) {
if (isUrgent && isImportant) {
return "critical";
}
return "normal";
}
Todos os pontos da aplicação podem chamar a mesma função.
Função e método são a mesma coisa?
Os dois representam comportamentos. Chamamos normalmente de método uma função associada a um objeto:
const title = " Estudar funções ";
const normalizedTitle = title.trim();
trim é um método da string. Já classifyPriority() é uma função definida pela
aplicação.
O que é escopo?
Escopo é a região do código em que um nome pode ser acessado:
function createMessage() {
const message = "Disponível apenas dentro da função";
return message;
}
console.log(message); // ReferenceError
O escopo impede que todos os valores fiquem disponíveis em todos os lugares. Isso reduz conflitos e protege detalhes internos.
O que é closure?
Closure é a capacidade de uma função lembrar o ambiente em que foi criada, mesmo depois que a função externa terminou:
function createCounter() {
let count = 0;
return function increment() {
count += 1;
return count;
};
}
const countExecution = createCounter();
countExecution(); // 1
countExecution(); // 2
count não é global e não pode ser alterado diretamente de fora. A função
increment continua tendo acesso a ele. Closures aparecem em contadores,
configurações, eventos, fábricas de funções e estado privado.
Onde isso aparece no projeto?
Nesta aula transformaremos validação, normalização e classificação em funções reutilizáveis. O laboratório mostrará os argumentos, o retorno de cada regra e um contador de execuções mantido por closure.
Objetivos
Ao final da aula, o aluno deverá conseguir:
- explicar por que funções reduzem duplicação;
- diferenciar declarar, referenciar e chamar uma função;
- utilizar parâmetros, argumentos e
return; - criar funções com declaração, expressão e arrow function;
- escolher nomes e responsabilidades claras;
- reconhecer escopo global, de função e de bloco;
- explicar diferenças relevantes entre
var,leteconst; - diferenciar função pura e efeito colateral;
- utilizar funções como valores e callbacks;
- construir e explicar uma closure.
Pré-requisitos
- Aulas 2.1 e 2.2 concluídas;
- variáveis, tipos, operadores e condições;
- noções de eventos e DOM;
- navegador, DevTools e editor de código.
Pergunta orientadora
Como transformar regras repetidas em unidades reutilizáveis sem expor todos os dados globalmente?
Roteiro sugerido
| Etapa | Duração |
|---|---|
| Introdução e anatomia de uma função | 20 min |
| Parâmetros, argumentos e retorno | 20 min |
| Formas de declarar e funções como valores | 20 min |
| Escopo e closures | 30 min |
| Laboratório | 25 min |
| Revisão | 5 min |
1. Declarar, referenciar e chamar
Declarar:
function calculateTotal(price, quantity) {
return price * quantity;
}
Referenciar:
const selectedRule = calculateTotal;
Chamar:
const total = calculateTotal(25, 3);
Ao referenciar, não usamos (). Ao chamar, usamos os parênteses e a função executa.
Isso é importante em eventos:
button.addEventListener("click", handleClick);
Passamos a referência handleClick. Se escrevêssemos handleClick(), executaríamos
a função imediatamente e passaríamos o resultado dela.
2. Parâmetros e argumentos
Parâmetros são os nomes definidos pela função:
function calculateTotal(price, quantity) {
return price * quantity;
}
Argumentos são os valores fornecidos na chamada:
calculateTotal(25, 3);
Aqui:
priceequantitysão parâmetros;25e3são argumentos.
Parâmetro padrão
Um valor padrão é usado quando o argumento é undefined:
function createGreeting(name = "estudante") {
return `Olá, ${name}!`;
}
createGreeting(); // "Olá, estudante!"
O padrão não substitui null, string vazia ou zero.
3. return
return encerra a função e entrega um valor:
function double(value) {
return value * 2;
}
Sem return explícito, o resultado é undefined:
function doubleWithoutReturn(value) {
value * 2;
}
doubleWithoutReturn(4); // undefined
return não é console.log
function calculateTotal(price, quantity) {
console.log(price * quantity);
}
Essa função exibe algo no console, mas não devolve o total. Prefira retornar a informação e decidir fora da regra onde apresentá-la:
const total = calculateTotal(25, 3);
output.textContent = String(total);
4. Retorno antecipado
Um retorno antecipado encerra casos inválidos antes da regra principal:
function validateTitle(title) {
const normalizedTitle = title.trim();
if (normalizedTitle.length < 3) {
return {
isValid: false,
message: "O título precisa ter pelo menos 3 caracteres.",
};
}
return {
isValid: true,
message: "Título válido.",
};
}
Esse padrão também é chamado de guard clause. Ele reduz níveis de aninhamento.
5. Declaração de função
function classifyEstimate(hours) {
if (hours <= 1) {
return "short";
}
if (hours <= 4) {
return "medium";
}
return "long";
}
Declarações possuem nome claro e podem ser chamadas antes da linha em que aparecem, porque sua declaração é içada durante a preparação do escopo.
showMessage();
function showMessage() {
console.log("Funcionou");
}
Esse comportamento é conhecido como hoisting. Ainda assim, organizar o código de forma previsível facilita a leitura.
6. Expressão de função
Uma função também pode ser armazenada em uma variável:
const classifyEstimate = function (hours) {
return hours <= 1 ? "short" : "long";
};
Aqui, a função é criada como valor e associada a classifyEstimate.
Não é possível chamar a constante antes de sua inicialização:
classifyEstimate(2); // ReferenceError
const classifyEstimate = function (hours) {
return "medium";
};
7. Arrow function
Uma arrow function oferece sintaxe compacta:
const double = (value) => {
return value * 2;
};
Quando existe apenas uma expressão, o retorno pode ser implícito:
const double = (value) => value * 2;
Com um único parâmetro, os parênteses são opcionais:
const normalize = value => value.trim();
Neste curso, manteremos os parênteses por consistência:
const normalize = (value) => value.trim();
Arrow function não substitui tudo
Arrow functions não criam seu próprio this e não possuem arguments. Isso será
relevante ao trabalhar com objetos e classes. Para regras pequenas e callbacks, elas
são frequentemente convenientes. Para funções principais, escolha a forma que
comunique melhor a intenção.
8. Uma responsabilidade por função
Compare:
function processTask(task) {
// valida, normaliza, classifica, salva, atualiza DOM e registra log
}
Com regras separadas:
function normalizeTitle(title) {
return title.trim();
}
function validateTitle(title) {
return title.length >= 3;
}
function classifyPriority(isUrgent, isImportant) {
// regra de classificação
}
Funções menores:
- recebem menos informações;
- são mais fáceis de nomear;
- podem ser reutilizadas;
- produzem testes mais simples;
- isolam mudanças.
Não existe uma quantidade mágica de linhas. Separe quando houver responsabilidades ou motivos de mudança diferentes.
9. Funções puras
Uma função pura:
- produz o mesmo resultado para os mesmos argumentos;
- não altera estado externo observável.
function calculateSubtotal(price, quantity) {
return price * quantity;
}
Não pura:
let total = 0;
function addToTotal(value) {
total += value;
}
A segunda depende e altera uma variável externa.
Interfaces sempre terão efeitos colaterais, como atualizar o DOM ou enviar uma requisição. A estratégia é manter as regras centrais puras e concentrar efeitos nas bordas:
const result = classifyPriority(isUrgent, isImportant); // regra pura
output.textContent = result; // efeito no DOM
10. Funções são valores
JavaScript permite armazenar, passar e retornar funções:
function executeRule(rule, value) {
return rule(value);
}
function double(value) {
return value * 2;
}
executeRule(double, 5); // 10
Uma função recebida por outra função é frequentemente chamada de callback.
Eventos utilizam callbacks:
form.addEventListener("submit", (event) => {
event.preventDefault();
});
O navegador chama o callback quando o evento ocorre.
11. Escopo global
Um nome declarado fora das funções e blocos pertence ao escopo externo do script:
const applicationName = "Painel de tarefas";
function showApplicationName() {
return applicationName;
}
A função interna consegue acessar o escopo externo.
Evite colocar todos os valores no escopo global. Muitos nomes globais aumentam conflitos, dependências ocultas e alterações acidentais.
12. Escopo de função
Parâmetros e variáveis declaradas dentro de uma função não ficam disponíveis fora:
function calculateTotal(price, quantity) {
const total = price * quantity;
return total;
}
console.log(total); // ReferenceError
Cada chamada cria seu próprio ambiente:
calculateTotal(10, 2);
calculateTotal(30, 4);
Os valores de uma chamada não substituem os valores internos da outra.
13. Escopo de bloco
let e const respeitam blocos:
if (true) {
const message = "Somente dentro do bloco";
let count = 1;
}
console.log(message); // ReferenceError
Blocos aparecem em if, for, while e chaves criadas deliberadamente.
14. var, let e const
var possui escopo de função, não de bloco:
if (true) {
var legacyMessage = "Ainda acessível fora do bloco";
}
console.log(legacyMessage);
Além disso, sua declaração sofre hoisting com inicialização em undefined, o que
pode esconder erros.
Prática moderna:
- utilize
constpor padrão; - utilize
letquando a associação precisar mudar; - evite
varem código novo.
15. Sombreamento de nomes
Um escopo interno pode declarar um nome igual ao externo:
const status = "global";
function inspectStatus() {
const status = "local";
return status;
}
O status local sombreia o externo dentro da função. Isso é permitido, mas nomes
iguais em escopos próximos podem dificultar a leitura.
16. Escopo léxico
JavaScript decide quais nomes uma função pode acessar com base no local onde ela foi definida, não no local onde foi chamada:
const label = "externa";
function createReader() {
const label = "interna";
return function readLabel() {
return label;
};
}
const readLabel = createReader();
readLabel(); // "interna"
Esse comportamento é a base das closures.
17. Closure passo a passo
function createExecutionCounter() {
let count = 0;
return function countExecution() {
count += 1;
return count;
};
}
Passos:
createExecutionCounter()criacount;- a função interna utiliza
count; - a função interna é retornada;
- mesmo após a função externa terminar, a função retornada lembra
count; - cada chamada atualiza o mesmo valor privado.
const counterA = createExecutionCounter();
const counterB = createExecutionCounter();
counterA(); // 1
counterA(); // 2
counterB(); // 1
Cada execução da função externa cria um novo ambiente.
Closure não é apenas função aninhada
Uma função aninhada demonstra closure quando mantém acesso ao ambiente léxico externo. O ponto importante é a preservação desse acesso, não apenas a presença de duas funções.
18. Fábrica de funções
Closures permitem configurar uma regra:
function createMinimumLengthValidator(minimumLength) {
return function validate(value) {
return value.trim().length >= minimumLength;
};
}
const validateTaskTitle = createMinimumLengthValidator(3);
const validatePassword = createMinimumLengthValidator(12);
Cada validador lembra sua própria configuração.
19. Composição de regras
Uma função pode coordenar funções menores:
function evaluateTask(rawTask) {
const title = normalizeTitle(rawTask.title);
const titleValidation = validateTitle(title);
const priority = classifyPriority(
rawTask.isUrgent,
rawTask.isImportant,
);
const size = classifyEstimate(rawTask.estimate);
return {
title,
titleValidation,
priority,
size,
};
}
evaluateTask não precisa repetir a implementação de cada regra. Ela organiza as
chamadas e reúne os resultados.
20. Laboratório guiado
Abra a biblioteca interativa de regras.
Etapa 1 — Execute as regras
- informe o título de uma tarefa;
- escolha urgência e importância;
- defina uma estimativa;
- clique em Executar biblioteca;
- observe argumentos e retornos.
Etapa 2 — Observe as funções puras
Execute duas vezes com os mesmos dados. As funções de normalização, validação e classificação devem retornar os mesmos resultados.
Etapa 3 — Observe a closure
O contador cresce a cada execução, mesmo sem ser global. Recarregue a página: um novo contador será criado e começará novamente em zero.
Etapa 4 — Inspecione o código
Localize em assets/main.js:
normalizeTitle;createMinimumLengthValidator;classifyPriority;classifyEstimate;evaluateTask;createExecutionCounter;- o callback do evento
submit.
21. Erros comuns
Esquecer os parênteses ao chamar
const result = classifyPriority;
Isso guarda a função. Para obter a classificação:
const result = classifyPriority(true, false);
Esquecer return
const double = (value) => {
value * 2;
};
Com chaves, o retorno precisa ser explícito:
const double = (value) => {
return value * 2;
};
Alterar argumentos sem necessidade
Objetos e arrays podem ser alterados dentro de uma função. Regras puras devem preferir criar um resultado em vez de modificar a entrada.
Criar funções com parâmetros demais
Muitos parâmetros podem indicar responsabilidades excessivas ou a necessidade de agrupar dados relacionados em um objeto. Estudaremos modelagem de objetos na Aula 2.5.
Depender de globais
let currentPriority = "low";
function classify() {
currentPriority = "critical";
}
Prefira retornar:
function classify() {
return "critical";
}
Usar closure sem necessidade
Closure é uma ferramenta, não uma obrigação. Se um valor pode ser passado claramente como argumento, essa pode ser a solução mais simples.
22. Boas práticas
- dê nomes verbais às funções;
- mantenha uma responsabilidade principal;
- prefira poucos parâmetros explícitos;
- retorne valores em vez de apenas imprimir;
- use retornos antecipados para casos inválidos;
- mantenha regras centrais puras quando possível;
- limite efeitos colaterais às bordas;
- use
constpor padrão; - documente o motivo, não traduza cada linha em comentário;
- teste entradas normais, limites e casos inválidos.
23. Exercícios
Exercício 1 — Saudação
Crie createGreeting(name = "estudante") e retorne uma mensagem.
Exercício 2 — Regra pura
Crie calculateProgress(completed, total) e retorne a porcentagem. Trate total
igual a zero.
Exercício 3 — Callback
Crie executeRule(rule, value) e passe uma função que normalize texto.
Exercício 4 — Escopo
Declare valores com const, let e var dentro de um bloco e registre quais ficam
disponíveis fora. Explique o resultado.
Exercício 5 — Closure
Crie createLimitedCounter(limit) que aumente até o limite e depois preserve o
valor máximo.
24. Desafio
Amplie o laboratório com:
- uma regra para detectar tarefa longa;
- um validador configurável de estimativa máxima;
- um segundo contador independente;
- um botão que reinicie apenas o estado visual, sem recarregar a página;
- testes manuais documentados para cada função pura.
25. Lista de verificação de conclusão
- Sei diferenciar declarar, referenciar e chamar.
- Sei explicar parâmetro e argumento.
- Entendo que
console.lognão substituireturn. - Consigo criar declaração e arrow function.
- Sei identificar uma função pura.
- Entendo escopo global, de função e de bloco.
- Sei por que evitamos
var. - Consigo explicar uma closure sem apenas repetir a definição.
- Testei o contador independente.
- Inspecionei o laboratório no DevTools.
26. Critérios de avaliação
| Critério | Pontos |
|---|---|
| Funções pequenas e bem nomeadas | 25 |
| Parâmetros e retornos corretos | 20 |
| Regras puras e composição | 20 |
| Uso e explicação de escopo | 15 |
| Closure funcionando e explicada | 15 |
| Acessibilidade e organização | 5 |
| Total | 100 |
Resumo
Nesta aula, aprendemos que:
- funções encapsulam responsabilidades;
- parâmetros recebem argumentos e
returnentrega resultados; - funções podem ser armazenadas, passadas e retornadas;
- escopo controla onde nomes estão disponíveis;
consteletrespeitam blocos;- funções puras tornam regras mais previsíveis;
- closures preservam acesso ao ambiente onde a função foi criada.
Próxima aula
Na Aula 2.4, utilizaremos arrays e seus métodos para mapear, filtrar, reduzir, ordenar e calcular métricas sem alterar os dados originais indevidamente.