Closures
Existe um conteúdo que geralmente é cobrado em entrevistas de emprego em Javascript: closures.
Closures são um recurso de programação funcional, que quando aplicadas em Javascript, nos ajudam a escrever código que não suja o escopo global. Isto é, evita a criação de variáveis globais.
Closures dependem bastante da compreensão dos escopos, se você não tiver entendido bem o capítulo de escopos, recomendo reler antes de continuar esse capítulo. Porém, se você tiver compreendido bem, vamos continuar.
Quando criamos uma função, ela cria a “bolha” do escopo dela.
function foo() {
let a = 1;
}
Qualquer código fora da função foo, não tem acesso ao escopo dela, portanto, não pode acessar a variável a.
Porém, se criarmos uma função dentro de foo, essa função poderá acessar o escopo de foo.
Então vamos criar uma função que incrementa e mostra a incrementado.
function foo() {
let a = 1;
function bar() {
a++;
console.log(a);
}
}
bar pode acessar o escopo de foo, além de qualquer código em foo.
E é isso, temos a nossa primeira closure!
A closure é uma função interior que tem acesso a variáveis de uma função exterior, através das regras de acesso de variáveis no escopo!
Portanto, bar é a nossa closure, pois ela é uma função interna (está dentro de foo).
Mas nesse caso, ainda não ficou claro a utilidade, porque não estamos usando bar.
O interessante vem agora: podemos retornar a função bar da função foo:
function foo() {
let a = 1;
function bar() {
a++;
console.log(a);
}
return bar;
}
E como estamos retornando uma função, podemos guardar essa função retornada, e invocar ela no código exterior (que está no contexto do escopo global):
function foo() {
let a = 1;
function bar() {
a++;
console.log(a);
}
return bar;
}
let bar = foo();
bar(); // 2
bar(); // 3
bar(); // 4
O código acima pode ser extremamente “bugante” (e é mesmo!), porém, vamos analisar com calma:
A função bar tem acesso ao escopo de foo
Invocamos a função foo, que retorna a função bar
E guardamos essa função retornada na variável bar.
A parte confusa é que: a função foo já terminou sua execução, com o return.
Porém, como bar tem acesso ao escopo de foo, esse escopo continua acessível, porém, acessível agora apenas para a função bar.
Se não tiver entendido, releia novamente, com calma. É um pouco esquisito mesmo.
Porém, é isso. Geralmente closures tem esse formato, e geralmente nós escrevemos closures sem perceber, quando escrevemos código assincrono por exemplo. (Código assincrono será mais detalhado na apostila 4 - Programação Assincrona).
Esse exemplo pode ter parecido meio bobo, mas com closures conseguimos ter encapsulamento, que é um principio importante em desenvolvimento de software.
Elas permitem esconder detalhes de implementação do código que está invocando a função.
Por exemplo, quem estiver chamando bar não precisa saber que estamos usando a variável a para realizar os incrementos.
Quando chamamos bibliotecas externas em nosso código, pelo menos em Javascript, essas bibliotecas geralmente usam closures para realizar operações internas, expondo pra você apenas as funções e métodos que a gente precisa.
Para mais informações, você pode consultar a documentação da MDN.