Closures em JavaScript: explicação simples e por baixo dos panos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Como funcionam os closures em JavaScript: análise com exemplo do Stack Overflow

\n\n

Se você já pesquisou no Google por "JavaScript closures" ou leu discussões no Stack Overflow, certamente viu a famosa pergunta: "How do JavaScript closures work?". Esse tópico acumulou milhares de visualizações e centenas de respostas — desde explicações simples até um mergulho profundo no motor V8. Por quê? Porque closures são um daqueles conceitos que inicialmente parecem mágica e depois se tornam sua superarma.

\n\n

Neste artigo, não vamos apenas repetir a teoria. Vamos olhar por baixo dos panos do JavaScript, analisar o que acontece na memória e finalmente entender por que esse código funciona exatamente dessa forma e não de outra. Prontos? Vamos lá.

\n\n

O que é um closure? Definição direta ao ponto

\n\n

Vamos direto ao essencial. Closure é uma função que "lembra" as variáveis do seu escopo externo, mesmo depois que essa função externa já terminou sua execução.

\n\n

Parece um feitiço? Vamos direto ao código. Aqui está um exemplo clássico que você encontrará no Stack Overflow:

\n\n
function createCounter() {\n    let count = 0;\n    return function() {\n        count++;\n        return count;\n    };\n}\n\nconst counter = createCounter();\nconsole.log(counter()); // 1\nconsole.log(counter()); // 2\nconsole.log(counter()); // 3
\n\n

Aqui, createCounter retorna uma função anônima. Essa função interna "fecha" a variável count. Mesmo depois que createCounter terminou (achamos que todas as variáveis dentro dela deveriam desaparecer), count continua existindo e mudando a cada chamada de counter(). Isso é um closure.

\n\n

Por baixo dos panos: o escopo léxico

\n\n

Para entender como isso funciona, precisamos olhar dentro do motor JavaScript. Toda função, quando criada, possui uma propriedade oculta [[Environment]]. Ela armazena uma referência ao escopo léxico (Lexical Environment) no qual a função foi criada.

\n\n

O escopo léxico é simplesmente um objeto que contém:

\n
    \n
  • Um registro de ambiente (Environment Record) — todas as variáveis e funções declaradas nesse escopo.
  • \n
  • Uma referência ao escopo externo (outer) — o escopo pai.
  • \n
\n\n

Quando chamamos uma função, um novo escopo léxico é criado para essa chamada. A função interna obtém acesso às variáveis da função externa através da cadeia de escopos (scope chain).

\n\n

Voltemos ao nosso exemplo. Quando declaramos createCounter, um escopo léxico global é criado. Dentro dele, temos a variável createCounter (a função) e counter (ainda undefined).

\n\n

Quando chamamos createCounter():

\n
    \n
  1. Um novo escopo léxico é criado para essa chamada. Dentro dele, a variável count = 0.
  2. \n
  3. Uma função anônima é criada. Seu [[Environment]] aponta para o escopo atual (onde count vive).
  4. \n
  5. A função é retornada e atribuída a counter.
  6. \n
\n\n

E aqui está a parte mais interessante: createCounter terminou, mas seu escopo léxico não é removido pelo coletor de lixo! Porque há uma referência a ele — o [[Environment]] da função interna. O escopo "permanece" na memória enquanto a própria função counter existir.

\n\n

Agora, toda vez que chamamos counter(), um novo escopo é criado para essa chamada, que, através da cadeia, enxerga aquele escopo antigo com count. É assim que obtemos o efeito de "memorização".

\n\n

Por que isso é importante? Cenários reais

\n\n

Closures não são apenas um conceito acadêmico. Eles são usados em toda parte. Vamos analisar alguns exemplos práticos.

\n\n

1. Variáveis privadas (encapsulamento)

\n\n

Em JavaScript, não existem verdadeiros

Blogs

Book Recommendations