Javascript: Mas afinal, o que são closures?

Stéphano Wallace
5 min readMar 26, 2018

--

Após alguns anos como desenvolvedor Javascript, percebi que independente do nível de experiência, muitos profissionais ainda possuem dúvidas a respeito do que são closures. Antes de explicar o que se trata, eu preciso dizer algo que considero importante: closures estão acontecendo em diversas partes do seu código e talvez você nem tenha percebido. Espero que após essa leitura, você consiga perceber onde elas ocorrem e ter um melhor entendimento do que está acontecendo no seu código.

O que são?

Closure é quando uma função é capaz de "lembrar" e acessar seu escopo léxico mesmo quando ela está sendo executada fora de seu escopo léxico.

Talvez essa explicação pareça um pouco confusa a princípio, para facilitar as coisas, acho importante falar um pouco sobre o que é o escopo léxico.

Sobre o escopo léxico

Para ficar claro o entendimento de closure, é importante saber alguns aspectos do escopo léxico. Claro, se você já sabe do que se trata, pode pular esta parte.

O escopo léxico é baseado nas variáveis e blocos de escopo definidos, por você, em tempo de escrita do código. Logo, ele é definido em tempo de compilação… pelo menos na maioria das vezes. Aqui, eu digo na maioria das vezes pois existem formas de 'burlar' esse escopo, como por exemplo, injetando código com declarações de variáveis de forma dinâmica*.

não entrarei em detalhes pois estas formas além de possuirem problemas de performance (não há otimização, como é feito na compilação), podem causar erros no código difíceis de serem mapeados.

Assim como blocos e funções podem ser inseridos dentro de outros blocos ou funções, o mesmo vale para escopos. Se uma variável não pôde ser encontrada no escopo imediato, a engine irá procurar por ela no escopo externo mais próximo. Essa busca irá continuar até que o escopo global seja alcançado. Se a variável não estiver no escopo global, um erro será emitido. Vamos a um exemplo para ilustrar melhor a situação:

var a = 2;
function soma() {
var b = 3;
return a + b;
}
console.log(soma()); // 5

Neste caso, temos dois escopos, o definido pela função soma e o global. Ao chamar a função, a engine encontra dentro do escopo da função soma apenas a variável b, como ela também necessita do valor da variável a, uma busca é feita no escopo externo, no caso, o global. Encontrado o valor, a operação segue normalmente, retornando o valor da soma das variáveis.

Voltando às closures…

Closure é a forma de fazer com que as variáveis dentro de uma função sejam privadas e persistentes.

function pai(){
var x = 1;
function filho(){
console.log(x);
x++;
}
return filho;
}

var contador = pai();
contador(); // 1
contador(); // 2
contador(); // 3

À primeira vista esse código pode parecer confuso, afinal, o que uma função está fazendo dentro da outra? E pra quê eu preciso retornar a função que eu declarei lá dentro? Calma… calma… tentarei explicar da melhor forma possível o que está acontecendo.

Como eu expliquei anteriormente, o escopo léxico faz com que a função filho tenha acesso às variáveis definidas pela função pai. Mas aí nós pegamos a função filho e a retornamos como um valor (no caso, retornamos a função à qual filho faz referência).

Após executarmos a função pai, nós atribuímos o valor que ela retorna (no caso, a função filho) para uma nova variável chamada contador. O simples fato de atribuirmos a função filho para uma variável, faz com que a função filho ainda seja necessária, da mesma forma que as variáveis que ela utiliza (o x no escopo da função pai) e isso faz com que a função pai também seja necessária. OU SEJA, esses dados não serão coletados pelo garbage collector da engine, fazendo com que a informação ali contida seja persistente e possa ser utilizada fora de seu escopo estático (!).

A função filho possui uma referência ao escopo da função pai, e a essa referência nós damos o nome de closure.

Pelo fato dessas variáveis serem persistentes, cada vez que eu executo a função contador, o valor da variável x será incrementada em um.

Módulos

Módulos são estruturas de código que fazem bom uso das closures, vamos a um exemplo que apresenta bem seu funcionamento:

function ModuloMatematico() {     
var x = 0;
function somaUm() {
x++;
console.log(x);
}
function subtraiUm() {
x--;
console.log(x);
}
return {
somaUm: somaUm,
subtraiUm: subtraiUm
};
}
var teste = ModuloMatematico();
teste.somaUm(); // 1
teste.somaUm(); // 2
teste.somaUm(); // 3
teste.subtraiUm(); // 2

Vamos reforçar os conceitos aprendidos:

  • Para que a criação do escopo interno e as closures aconteçam, primeiramente precisamos executar a função ModuloMatematico.
  • A função ModuloMatematco irá retornar um objeto com referências para as funções internas somaUm e subtraiUm.
  • A variável x não pode ser acessada fora do escopo de ModuloMatematico, já que ela é privada.
  • Como a variável x também é persistente, graças à closure. Sempre que chamamos pelas funções somaUm e subtraiUm dentro do nosso módulo, o valor dela será alterado, sem ser reiniciado.
  • Podemos dizer que esse objeto retornado pela função ModuloMatemático é uma API pública para nosso módulo.

A forma com que representei esse módulo, baseado em função, não é um padrão estaticamente reconhecido, ou seja, sua semântica não é reconhecida em tempo de compilação. Isso deixa uma brecha para que você possa alterar a API do seu módulo em tempo de execução.

No ES6+, nós tivemos a adição de API's de módulos estáticas, que faz a verificação de membros importados de um determinado módulo em tempo de compilação, lançando um erro caso não exista.

Esse novo sistema de módulos do ES6, trata cada arquivo da sua aplicação como um módulo separado. Cada módulo pode importar novos módulos ou membros específicos de uma API, assim como exportar seus próprios membros, que serão públicos.

Isso é tudo pessoal!

Closures sem dúvida podem parecer confusas a princípio, mas espero que esse texto tenha feito com que você perceba melhor como elas funcionam. Recomendo que você dê uma segunda olhada em algum código seu e tente perceber onde as closures estão presentes, é um ótimo exercício para reforçar o conceito.

--

--

Stéphano Wallace

Desenvolvedor Front-end. Viciado em séries, filmes, música e jogos