# Aula 2.3 - Funções, escopo e closures

## 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`](../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.

```js
function greetStudent() {
  return "Olá, estudante!";
}

const message = greetStudent();
```

Neste exemplo:

- `greetStudent` é o nome da função;
- `()` executa, ou chama, a função;
- `return` entrega o resultado;
- `message` recebe 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:

```js
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:

```js
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:

```js
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:

```js
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:

1. explicar por que funções reduzem duplicação;
2. diferenciar declarar, referenciar e chamar uma função;
3. utilizar parâmetros, argumentos e `return`;
4. criar funções com declaração, expressão e arrow function;
5. escolher nomes e responsabilidades claras;
6. reconhecer escopo global, de função e de bloco;
7. explicar diferenças relevantes entre `var`, `let` e `const`;
8. diferenciar função pura e efeito colateral;
9. utilizar funções como valores e callbacks;
10. 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:

```js
function calculateTotal(price, quantity) {
  return price * quantity;
}
```

Referenciar:

```js
const selectedRule = calculateTotal;
```

Chamar:

```js
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:

```js
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:

```js
function calculateTotal(price, quantity) {
  return price * quantity;
}
```

Argumentos são os valores fornecidos na chamada:

```js
calculateTotal(25, 3);
```

Aqui:

- `price` e `quantity` são parâmetros;
- `25` e `3` são argumentos.

### Parâmetro padrão

Um valor padrão é usado quando o argumento é `undefined`:

```js
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:

```js
function double(value) {
  return value * 2;
}
```

Sem `return` explícito, o resultado é `undefined`:

```js
function doubleWithoutReturn(value) {
  value * 2;
}

doubleWithoutReturn(4); // undefined
```

### `return` não é `console.log`

```js
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:

```js
const total = calculateTotal(25, 3);
output.textContent = String(total);
```

## 4. Retorno antecipado

Um retorno antecipado encerra casos inválidos antes da regra principal:

```js
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

```js
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.

```js
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:

```js
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:

```js
classifyEstimate(2); // ReferenceError

const classifyEstimate = function (hours) {
  return "medium";
};
```

## 7. Arrow function

Uma arrow function oferece sintaxe compacta:

```js
const double = (value) => {
  return value * 2;
};
```

Quando existe apenas uma expressão, o retorno pode ser implícito:

```js
const double = (value) => value * 2;
```

Com um único parâmetro, os parênteses são opcionais:

```js
const normalize = value => value.trim();
```

Neste curso, manteremos os parênteses por consistência:

```js
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:

```js
function processTask(task) {
  // valida, normaliza, classifica, salva, atualiza DOM e registra log
}
```

Com regras separadas:

```js
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:

1. produz o mesmo resultado para os mesmos argumentos;
2. não altera estado externo observável.

```js
function calculateSubtotal(price, quantity) {
  return price * quantity;
}
```

Não pura:

```js
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:

```js
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:

```js
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:

```js
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:

```js
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:

```js
function calculateTotal(price, quantity) {
  const total = price * quantity;
  return total;
}

console.log(total); // ReferenceError
```

Cada chamada cria seu próprio ambiente:

```js
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:

```js
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:

```js
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 `const` por padrão;
- utilize `let` quando a associação precisar mudar;
- evite `var` em código novo.

## 15. Sombreamento de nomes

Um escopo interno pode declarar um nome igual ao externo:

```js
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:

```js
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

```js
function createExecutionCounter() {
  let count = 0;

  return function countExecution() {
    count += 1;
    return count;
  };
}
```

Passos:

1. `createExecutionCounter()` cria `count`;
2. a função interna utiliza `count`;
3. a função interna é retornada;
4. mesmo após a função externa terminar, a função retornada lembra `count`;
5. cada chamada atualiza o mesmo valor privado.

```js
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:

```js
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:

```js
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](../exemplos/aula-2.3/index.html).

### Etapa 1 — Execute as regras

1. informe o título de uma tarefa;
2. escolha urgência e importância;
3. defina uma estimativa;
4. clique em **Executar biblioteca**;
5. 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

```js
const result = classifyPriority;
```

Isso guarda a função. Para obter a classificação:

```js
const result = classifyPriority(true, false);
```

### Esquecer `return`

```js
const double = (value) => {
  value * 2;
};
```

Com chaves, o retorno precisa ser explícito:

```js
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

```js
let currentPriority = "low";

function classify() {
  currentPriority = "critical";
}
```

Prefira retornar:

```js
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 `const` por 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.log` não substitui `return`.
- [ ] 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 `return` entrega resultados;
- funções podem ser armazenadas, passadas e retornadas;
- escopo controla onde nomes estão disponíveis;
- `const` e `let` respeitam 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.
