Dia 7 — Funções, escopo e closures

Informatica · Conteudo · publicado em 02/10/2026
Dia 7 de 13

Funções, escopo e closures

Aula 1

Declarar, chamar e passar dados

Declarar função: parâmetro, argumento e return

Uma função é um bloco com nome que guarda um trecho de código. A assinatura é function nome(parametro) { corpo }: o nome é como se chama, o parâmetro é o que a função recebe e o que se chama na invocação é o argumento.

function somar(a, b) {
  return a + b;
}

const total = somar(2, 3);   // 5

return devolve um valor e encerra a função na hora. O que vem depois do return não roda. Sem return, a função devolve undefined — e é isso que console.log(minhaFuncao()) mostra quando a função não devolve nada.

Chamar função: o nome, a invocação e o que volta

Função declarada com nome fica disponível no escopo pelo próprio nome da função, e a sintaxe de chamar função é nome, parênteses e argumentos: cumprimentar('Bia'). Escrever só o nome não executa nada: cumprimentar é a referência à função, cumprimentar('Bia') é a execução. Nos manuais a mesma ação aparece com outras palavras — invocar, executar, disparar — e são sinônimos: abrir um quadro novo, casar cada argumento com o parâmetro da mesma posição e rodar o corpo até o fim ou até o return.

const f = somar;   // o nome da função é um valor: dá para copiar
f(2, 3);            // mesmo resultado de somar(2, 3)
somar.name;         // 'somar' — o nome também é inspecionável

Quem não tem nome não pode ser invocado por nome: só existe no lugar onde foi guardada. É a função anônima, que volta na aula seguinte como função seta.

function cumprimentar(nome) {
  console.log('olá, ' + nome);
}

cumprimentar('Bia');                 // imprime e nada é devolvido
console.log(cumprimentar('Caio'));   // imprime e devolve undefined

O console.log dentro da função é o caso mais direto de função sem retorno: o efeito acontece na tela e o valor que volta é undefined. Envolver a chamada em console.log faz a impressão aparecer duas vezes — uma do efeito da função, outra do valor devolvido.

Parâmetro, argumento e o que a função devolve

O parâmetro é a caixa; o argumento é o que foi colocado dentro dela. Duas funções com o mesmo nome de parâmetro não têm nada em comum: quem chama não sabe qual é o nome, só a posição.

function apresentar(nome, sobrenome) {
  return `${nome} ${sobrenome}`;
}

apresentar('Ana', 'Souza');    // 'Ana Souza'
apresentar();                  // 'undefined undefined'

return pode devolver um objeto pronto, não só um escalar: return { nome, ativo: true } entrega a estrutura inteira e quem recebe usa direto.

Efeito colateral

A função que não devolve nada e age no mundo é o tipo mais comum de código em produção: ela imprime, escreve no DOM, salva no banco. O que ela faz é efeito colateral, e vale a regra de manter o efeito em uma função só, para o resto do programa ser testável e a ordem das chamadas ficar explícita.

AssinaturaDevolveServe para
function soma(a, b) { }o returncalcular
function criarItem(x) { }undefinedalterar algo que já existe
function salvar(x) { }Promise (quando async)falar com servidor
const soma = (a, b) => { }o returnatalho, sem nome e sem this

Função declarada com function é içada: pode ser chamada em uma linha antes da linha em que foi escrita. Função atribuída a const só existe depois da atribuição, porque é um valor como qualquer outro.

Parâmetros padrão e rest

function somar(a, b = 0) usa o padrão quando o argumento vem undefined — null e 0 não aciona o padrão, passam direto. Isso permite chamar com menos argumentos sem checar nada.

O rest parameter ...rest junta os argumentos que sobram num array, com const para não virar outro parâmetro:

function somarTudo(...numeros) {
  return numeros.reduce((total, n) => total + n, 0);
}

somarTudo(1, 2, 3);   // 6

console.log é função, e a diferença aparece no retorno: console.log('a') devolve undefined, enquanto console.log('a' + 'b') imprime o texto. E atenção ao nome: dentro de uma função, console existe; a variável que guarda o resultado do console.log precisa de outro nome, ou o próprio console some do escopo dali para fora.

Exemplo

'use strict';
// Exemplo: declaring, chamando e o que cada funcao devolve.

function somar(a, b) {
  return a + b;
}
console.log('somar(2, 3) =', somar(2, 3));

// O parametro e a caixa; o argumento e o que foi colocado dentro dela.
function apresentar(nome, sobrenome) {
  return `${nome} ${sobrenome}`;
}
console.log(apresentar('Ana', 'Souza'));
console.log('sem argumento:', JSON.stringify(apresentar()));

// return encerra a funcao: o que vem depois nao roda.
function primeiroPar(numero) {
  if (numero % 2 === 0) {
    return 'par';
  }
  return 'impar';
}
console.log('primeiroPar(4):', primeiroPar(4), '| primeiroPar(7):', primeiroPar(7));

// Funcao sem return devolve undefined. E o que o console mostra.
function cumprimentar(nome) {
  console.log('   ola, ' + nome);
}
console.log('cumprimentar devolve:', cumprimentar('Bia'));

// Efeito colateral: a funcao age no mundo e nao devolve nada.
const carrinho = [];
function adicionar(produto) {
  carrinho.push(produto);
  console.log('   adicionou ' + produto + ' | itens: ' + carrinho.length);
}
adicionar('arroz');
adicionar('feijao');
console.log('carrinho final:', carrinho.join(', '));

// Parametro padrao: so o undefined dispara o padrao. null e 0 passam direto.
function multiplicar(a, b = 2) {
  return a * b;
}
console.log('multiplicar(5):', multiplicar(5));
console.log('multiplicar(5, 3):', multiplicar(5, 3));
console.log('multiplicar(5, null):', multiplicar(5, null), '(null nao dispara o padrao)');
console.log('multiplicar(5, 0):', multiplicar(5, 0), '(0 nao dispara o padrao)');

// Rest parameter: o que sobrar vira array.
function somarTudo(...numeros) {
  return numeros.reduce((total, n) => total + n, 0);
}
console.log('somarTudo(1, 2, 3):', somarTudo(1, 2, 3));
console.log('somarTudo() sem argumento:', somarTudo());
console.log('o rest e sempre um array:', Array.isArray(((...x) => x)(1, 2)));

// A funcao declarada e icada: pode ser chamada antes da linha em que existe.
console.log('chamada antes da declaracao:', triple(7));
function triple(n) {
  return n * 3;
}

// A atribuida a const so existe depois da atribuicao: e um valor.
const metade = (n) => n / 2;
console.log('metade(9):', metade(9));

// Return de estrutura: a funcao devolve o objeto inteiro.
function criarItem(nome) {
  return { nome, ativo: true };
}
const item = criarItem('teclado');
console.log('criarItem:', item.nome, '| ativo:', item.ativo);

// Nome de parametro nao viaja para quem chama.
function apresentar2(a, b) {
  return `${a} ${b}`;
}
console.log('o nome do parametro e local:', apresentar2('Ana', 'Souza'));

Saída real

somar(2, 3) = 5
Ana Souza
sem argumento: "undefined undefined"
primeiroPar(4): par | primeiroPar(7): impar
   ola, Bia
cumprimentar devolve: undefined
   adicionou arroz | itens: 1
   adicionou feijao | itens: 2
carrinho final: arroz, feijao
multiplicar(5): 10
multiplicar(5, 3): 15
multiplicar(5, null): 0 (null nao dispara o padrao)
multiplicar(5, 0): 0 (0 nao dispara o padrao)
somarTudo(1, 2, 3): 6
somarTudo() sem argumento: 0
o rest e sempre um array: true
chamada antes da declaracao: 21
metade(9): 4.5
criarItem: teclado | ativo: true
o nome do parametro e local: Ana Souza
Aula 2

Arrow function, escopo e closures

Arrow function, escopo e closure

A arrow function é a forma curta: const soma = (a, b) => a + b. Sem function, sem nome, com o corpo em uma linha quando é uma expressão só. Com bloco, o return continua obrigatório.

O nome completo é função seta, e a seta é justamente o que dá o nome: =>. A outra metade do nome é o que a arrow tem menos — ela é sempre função anônima, sem nome, ou seja, ela só existe no lugar onde foi guardada.

const somar = (a, b) => a + b;
const log = (msg) => { console.log(msg); };
const zero = () => 0;               // parênteses obrigatórios mesmo vazio

A regra do parêntesis: com um parâmetro, msg => msg funciona. Com zero ou com dois, os parênteses voltam.

O parâmetro padrão é o valor que entra quando o argumento não vem: const saudar = (nome = 'visitante') => .... Ele só age quando o argumento é undefined — passar '' ou 0 usa o que foi passado, não o padrão.

Callback e função como valor

Função é valor. Isso significa que ela pode ser guardada em variável, passada como argumento e devolvida por outra função. Passar função para outra função executar é o que se chama função de callback, e é a base de map, filter, addEventListener e de todo o resto do curso.

const dobrar = (n) => n * 2;
const aplicar = (valor, funcao) => funcao(valor);   // funcao como parametro
aplicar(5, dobrar);   // 10

Arrow function não tem this próprio: o this dela é o do módulo onde foi escrita. Em código de hoje isso é o desejado quase sempre, e é a razão de a arrow ter substituído a função comum em callback.

Escopo: onde a variável pode ser usada

let e const nascem no bloco de chaves em que foram declarados e morrem no fim dele. Variável declarada fora de todo bloco fica no escopo global e é acessível de qualquer lugar — no navegador, isso é window.

O outro lado do escopo é o escopo de função: uma variável local declarada dentro de uma função só existe ali dentro, e nem a função que a chamou consegue enxergá-la. É a diferença entre global_ e local no exemplo.

const global_ = 'visivel de tudo';
function externa() {
  const local = 'so aqui dentro';   // variavel local: invisivel de fora
  function interna() {
    console.log(local);   // funciona: a interna foi criada dentro da externa
  }
}

interna não existe fora de externa, e local não existe fora dela. A ordem de declaração também importa: usar uma const antes da linha em que ela é declarada é ReferenceError, não undefined.

Closure: a função carrega o ambiente de onde nasceu

Closure não é palavra reservada; é o nome do que acontece quando uma função guardada em variável mantém acesso às variáveis do escopo onde foi criada, mesmo depois de a função que a criou ter terminado. O termo é fechamento, e o nome vem do fato de a função fechar sobre o ambiente onde nasceu.

function criarContador() {
  let contagem = 0;              // mora dentro, ninguem de fora alcanca
  return function somar() {
    contagem++;
    return contagem;
  };
}

const contador = criarContador();
contador();   // 1
contador();   // 2
contador();   // 3

O detalhe que importa: a variável contagem é preservada entre as chamadas. Não é que a variável de fora mudou — ela nunca foi exposta. Cada criarContador() cria um contagem novo e independente, então dois contadores não se misturam. É assim que se escreve estado privado sem classe.

const a = criarContador();
const b = criarContador();
a(); a();   // 1, 2
b();        // 1 — o estado de b é o seu, nao o de a

Memoizar: a closure que guarda o resultado

A mesma closure que preserva um contador também serve para guardar um resultado caro, em vez de recalculá-lo a cada chamada. É o que se chama memoizar, e o padrão é sempre o mesmo: uma chave, um mapa de resultados já calculados e a checagem no começo.

function memoizar(calcular) {
  const cache = new Map();
  return (chave) => {
    if (cache.has(chave)) {
      return cache.get(chave);   // ja foi calculado, devolve pronto
    }
    const resultado = calcular(chave);
    cache.set(chave, resultado);
    return resultado;
  };
}

const caro = memoizar((n) => { /* calculo pesado aqui */ return n * 2; });

O rest parameter entra justamente aí: (...numeros) junta os argumentos que sobram num array, e o spread ... é o mesmo operador nos dois papéis — aqui ele junta os argumentos, no dia 8 ele despeja uma lista. É a diferença entre receber e enviar.

Esse é o mesmo mecanismo que sustenta addEventListener com contador de cliques, cache de resultado e memoização.

A armadilha clássica do laço é a mesma coisa pelo outro lado: com var, todas as voltas do for compartilham a mesma variável, e a closure criada em cada volta aponta para ela. As dez funções veilhes enxergam o mesmo i, já com o valor final. Com let, cada volta cria um ambiente novo, e cada closure guarda o seu.

Exemplo

'use strict';
// Exemplo: arrow function, callback, escopo e closure.
// O ponto central: a variavel interna e PRESERVADA entre as chamadas.
// Nao e a variavel de fora que mudou — a de fora nunca foi exposta.

function somar(a, b) {
  return a + b;
}
const somarSeta = (a, b) => a + b;
const mensagem = (msg) => { console.log('   seta com bloco:', msg); };
const zero = () => 0;   // parenteses obrigatorios mesmo vazio

console.log('function:', somar(2, 3), '| arrow:', somarSeta(2, 3));
mensagem('ok');
console.log('zero():', zero());

// Funcao como valor: guardada em variavel e passada como argumento.
const dobrar = (n) => n * 2;
const aplicar = (valor, funcao) => funcao(valor);   // funcao no parametro
console.log('aplicar(5, dobrar):', aplicar(5, dobrar));
console.log('uma funcao e um valor:', typeof dobrar);

// Callback: guardar os dois lados para o laco do dia 8.
const numeros = [1, 2, 3];
console.log('map com callback:', numeros.map(dobrar).join(', '));

// --- ESCOPO -------------------------------------------------------------
const global_ = 'visivel de todo o codigo';

function externa() {
  const local = 'so existe dentro de externa';
  console.log('dentro de externa:', local, '|', global_);

  function interna() {
    // A interna foi criada dentro da externa: enxerga o escopo dela.
    console.log('dentro de interna:', local);
  }
  interna();
}
externa();

// O global e o mesmo objeto que a janela do navegador.
console.log('globalThis existe:', typeof globalThis === 'object');

// --- CLOSURE ------------------------------------------------------------
function criarContador() {
  let contagem = 0;                      // estado privado, fora de alcance
  return function somar() {
    contagem++;                          // mexe no estado, nao numa variavel externa
    return contagem;
  };
}

const contador = criarContador();
console.log('contador():', contador());
console.log('contador():', contador());
console.log('contador():', contador());

// A prova de que o valor foi PRESERVADO, e nao accomplishmento de fora:
// a funcao devolve sempre o estado guardado, sem ninguem setar nada.
console.log('o estado e da propria funcao:', contador());

// Cada chamada de criarContador cria um estado novo e independente.
const a = criarContador();
const b = criarContador();
a();
a();
b();
console.log('a():', a(), '| b():', b(), '| os dois nao se misturam');

// A variavel de fora nao existe — o nome contagem nao existe neste arquivo.
console.log('existe uma variavel global "contagem"?', typeof globalThis.contagem);

// Funcao que devolve funcao: a forma de montar um filtro configuravel.
function multiplicador(fator) {
  return (n) => n * fator;   // o fator fica preso na funcao devolvida
}
const por10 = multiplicador(10);
const por3 = multiplicador(3);
console.log('por10(4):', por10(4), '| por3(4):', por3(4));

// --- A ARMADILHA DO LACO -------------------------------------------------
// Com var, as tres funcoes apontam para a MESMA variavel i, que ja terminou.
const comVar = [];
for (var i = 0; i < 3; i++) {
  comVar.push(() => i);
}
console.log('com var, as tres devolvem:', comVar.map((f) => f()));

// Com let, cada volta cria um i novo, e cada closure guarda o seu.
const comLet = [];
for (let j = 0; j < 3; j++) {
  comLet.push(() => j);
}
console.log('com let, cada uma guardou o seu:', comLet.map((f) => f()));

// A forma de escopo de bloco tambem funciona, e e explicita.
const comBloco = [];
for (let k = 0; k < 3; k++) {
  const copia = k;
  comBloco.push(() => copia);
}
console.log('copiando a variavel antes:', comBloco.map((f) => f()));

Saída real

function: 5 | arrow: 5
   seta com bloco: ok
zero(): 0
aplicar(5, dobrar): 10
uma funcao e um valor: function
map com callback: 2, 4, 6
dentro de externa: so existe dentro de externa | visivel de todo o codigo
dentro de interna: so existe dentro de externa
globalThis existe: true
contador(): 1
contador(): 2
contador(): 3
o estado e da propria funcao: 4
a(): 3 | b(): 2 | os dois nao se misturam
existe uma variavel global "contagem"? undefined
por10(4): 40 | por3(4): 12
com var, as tres devolvem: [ 3, 3, 3 ]
com let, cada uma guardou o seu: [ 0, 1, 2 ]
copiando a variavel antes: [ 0, 1, 2 ]