Entenda o que são Closures em JavaScript

10:21

Encontramos uma publicação do blog Javascript Brasil que explica, com facilidade, o que são Closures em JavaScript. Closures permitem uma programação criativa e concisamente. Eles são usados frequentemente no JavaScript, sendo fáceis de se entender e atraentes para serem usados em seus projetos. É bastante comum encontrar closures em códigos jQuery e Node.js. Abaixo você pode conferir um clássico exemplo de closure em jQuery.

$(function () {    var selections = [];    $(".niners").click(function () {        selections.push(this.prop ("name"));    });});

A equipe da Turbosite repassamos esse conhecimento com a postagem abaixo.

 

O que é um closure?

Um closure é uma função interior que tem acesso a variáveis de uma função exterior – cadeia de escopo. O closure tem três cadeias de escopo: o seu próprio escopo; escopo externo, tendo acesso as variáveis da função exterior; e tem acesso as variáveis globais.

A função interior tem acesso não somente as variáveis da função exterior, mas também aos parâmetros dela. Note que a função interior não pode chamar o objeto(arguments) da função exterior, entretanto, pode chamar parâmetros da função externa diretamente.

Você cria um closure adicionando uma função dentro de outra função.

Regra dos Closures e Efeitos Colaterais

1. Closures tem acesso a variável das funções exteriores mesmo após o retorno da função exterior

Uma da mais importante e delicada característica dos closures é que a função interior continua tendo acesso as variáveis da função exterior mesmo após ela ter retornado.  Quando funções no JavaScript executam, elas usam a mesma cadeia de escopo que estava em vigor quando foram criadas. Isso significa que mesmo depois da função exterior retornar, a função interior continua tendo acesso as variáveis da função exterior. Portanto, você pode chamar a função interior depois em seu programa. Este exemplo demonstra isso:

2. Closures armazenam referências para as variáveis da função exterior

Eles não armazenam o valor real. Closures ficam mais interessantes quando o valor da variável da função exterior muda antes que o closure seja chamado, e esta poderosa característica pode ser aproveitada de formas criativas, como este exemplo de variável privada primeiramente demonstrada por Douglas Crockford:

 

3. Closures que deram errado

Por causa dos closures terem acesso ao valor atualizado das variáveis das funções exteriores, eles podem também conduzir a erros quando a variável da função exterior muda com um loop for. Assim:

//Este exemplo é explicado em detalhe abaixo (logo após este bloco de código)function celebrityIDCreator (theCelebrities) {    var i;    var uniqueID = 100;    for (i = 0; i < theCelebrities.length; i += 1) {        theCelebrities[i]["id"] = function () {            return uniqueID + i;        }    }     return theCelebrities;} var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}]; var createIdForActionCelebs = celebrityIDCreator(actionCelebs); var stalloneID = createIdForActionCelebs[0]; console.log(stalloneID.id()); //103

No exemplo anterior, quando a função anônima é chamada, o valor de i é 3 (a contagem do array e então seus incrementos). O número 3 foi adicionado ao uniqueID para criar 103 para TODOS os celebrityID. Então cada posição no array retornado obteve o id = 103, ao invés do valor pretendido 100, 101, 102.

A razão para isso acontecer foi porque, assim como nós discutimos no exemplo anterior, o closure (função anônima neste exemplo) teve acesso a variável da função exemplo por referência, não por valor. Então, igual ao exemplo anteriormente mostrado onde nós acessamos o valor atualizado da variável com o closure, neste exemplo, similarmente acessamos a variável i quando ela foi alterada, depois que a função exterior rodou o loop for inteiro e retornou o último valor, que foi 103.

Para consertar este efeito colateral (bug) nos closures, você pode usar uma Expressão de Função Imediatamente Invocada (IIFE – Immediately Invoked Function Expression), como no exemplo seguinte: