Os fechamentos de JavaScript se formam quando uma função retém acesso a variáveis de seu escopo externo, mesmo depois que a função externa termina de ser executada. Este mecanismo depende do escopo lexical, onde a função interna lembra o ambiente em que foi criada.
Como funcionam os fechamentos de JavaScript
Os fechamentos ocorrem porque as funções JavaScript criam um instantâneo de suas variáveis circundantes. Quando uma função interna faz referência a variáveis de uma função externa, essas variáveis permanecem na memória enquanto a função interna existir. Esse comportamento decorre do fato de que funções em JavaScript são objetos de primeira classe que carregam seu ambiente léxico.
Considere a pilha de contexto de execução. Cada chamada de função cria um novo contexto, mas os encerramentos preservam referências a variáveis em contextos pais. A coleta de lixo ignora essas variáveis porque os fechamentos ativos ainda fazem referência a elas.
Exemplos práticos de fechamentos de JavaScript
Um exemplo básico demonstra a retenção variável:
function outerFunction() {
let outerVar = 'Hello';
function innerFunction() {
console.log(outerVar);
}
return innerFunction;
}
const closureExample = outerFunction();
closureExample(); // Outputs: Hello
A função interna mantém acesso a outerVar após a conclusão de outerFunction.
Outro padrão comum cria um contador:
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Cada chamada para counter incrementa a variável de contagem preservada.
Variáveis privadas tornam-se possíveis através de encerramentos:
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) {
balance += amount;
return balance;
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(100);
console.log(account.deposit(50)); // 150
O código externo não pode modificar diretamente o equilíbrio, impondo o encapsulamento.
Use casos de encerramentos em JavaScript
Os fechamentos permitem ocultar dados em módulos. Os desenvolvedores agrupam variáveis dentro de expressões de função invocadas imediatamente para evitar poluição global enquanto expõem apenas os métodos necessários.
Os manipuladores de eventos frequentemente dependem de encerramentos para manter o estado em operações assíncronas. Ao anexar ouvintes em loops, os encerramentos capturam os valores de iteração corretos:
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
console.log(i);
});
}
Usar let garante que cada ouvinte feche seu próprio valor i.
As fábricas de funções aproveitam os fechamentos para gerar funções personalizadas. Um utilitário de registro pode produzir registradores com prefixos predefinidos enquanto compartilha a configuração interna.
A memorização se beneficia dos fechamentos, armazenando os resultados computados em um objeto de cache fechado, evitando cálculos repetidos e caros em funções recursivas como Fibonacci.
Melhores práticas para usar fechamentos JavaScript
Limite o escopo de fechamento a variáveis essenciais para reduzir a sobrecarga de memória. Grandes fechamentos contendo referências desnecessárias podem atrasar a coleta de lixo e aumentar o uso do heap.
Prefira const e deixe var dentro dos fechamentos para evitar problemas de elevação não intencionais que alterem os valores capturados.
Documente claramente os módulos baseados em fechamento, especificando quais variáveis permanecem privadas e como ocorrem as transições de estado.
Teste fechamentos em condições assíncronas para confirmar se os valores capturados correspondem às expectativas, especialmente ao lidar com tempos limite ou promessas.
Evite criar fechamentos dentro de loops sem variáveis com escopo de bloco, pois as declarações var mais antigas fazem com que todas as iterações compartilhem a mesma referência.
Monitore o desempenho em aplicativos de longa execução, limpando periodicamente os caches mantidos fechados quando os dados ficarem obsoletos. Use referências fracas quando houver suporte para permitir a limpeza automática de objetos grandes.
Combine encerramentos com padrões modernos, como classes, quando as necessidades de encapsulamento excederem a simples retenção de estado, garantindo que o código permaneça sustentável à medida que a complexidade aumenta.