Skip to content
Latchkey

O Que É um Closure? Uma Função Que Lembra Seu Escopo

Um closure é uma função pareada com as variáveis ao redor que ela capturou quando foi definida, de modo que possa continuar usando essas variáveis mesmo depois que aquele escopo terminou.

Quando uma função é definida dentro de outra função, ela pode referenciar as variáveis da função externa. Um closure é essa função interna junto com um vínculo vivo a essas variáveis capturadas. Mesmo depois que a função externa retornou, o closure as mantém vivas e pode ler ou atualizá-las, o que torna os closures poderosos e ocasionalmente surpreendentes.

O que os closures capturam

Um closure captura referências às variáveis em escopo onde ele foi criado, não apenas seus valores naquele instante. Então, se uma variável capturada muda depois, o closure enxerga o novo valor. É isso que permite aos closures manter estado privado.

Por que closures são úteis

  • Estado privado escondido atrás de uma função, como um contador ou cache.
  • Funções fábrica que produzem funções especializadas.
  • Callbacks que lembram o contexto de onde foram criados.
  • Aplicação parcial, fixando alguns argumentos com antecedência.

A clássica armadilha do loop

Um bug frequente é criar closures em um loop que todos capturam a mesma variável do loop. Cada closure enxerga o valor final em vez do valor na sua iteração, a menos que a linguagem ou um novo binding limite o escopo da variável por iteração.

Closures e memória

Como um closure mantém suas variáveis capturadas vivas, ele pode reter objetos grandes involuntariamente, impedindo a coleta de lixo. Um closure armazenado em uma lista ou registro de vida longa é uma fonte comum e silenciosa de vazamentos de memória.

Um exemplo rápido

Um makeCounter() que retorna uma função que incrementa um count local cria um closure: cada função retornada tem seu próprio count privado que persiste entre chamadas.

Closures em CI

Closures que retêm objetos capturados grandes podem crescer em memória ao longo de uma execução longa de testes e disparar OOM kills em runners pequenos. Runners maiores (Latchkey) dão mais folga e repetem automaticamente OOM kills transitórios, mas um closure que captura demais ainda merece uma correção.

Principais conclusões

  • Um closure é uma função mais as variáveis capturadas de onde ela foi definida.
  • Ele captura referências, então enxerga mudanças posteriores e pode manter estado privado.
  • Closures podem reter objetos grandes e vazar memória se armazenados por muito tempo.

Guias relacionados