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.
function showName (firstName, lastName) { var nameIntro = "Your name is "; //esta função interior tem acesso as variáveis da função exterior, incluindo os parâmetros function makeFullName () { return nameIntro + firstName + " " + lastName; } return makeFullName (); } showName ("Michael", "Jackson"); //Your name is Michael Jackson
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:
function celebrityName (firstName) { var nameIntro = "This celebrity is "; //essa função interior tem acesso as variáveis da função exterior, incluindo os parâmetros function lastName (theLastName) { return nameIntro + firstName + " " + theLastName; } return lastName; } var mjName = celebrityName ("Michael"); //Nesta conjuntura, a função exterior celebrityName foi retornada //O closure (lastName) é chamado aqui depois da função exterior ter retornado acima //Sim, o closure continua tendo acesso as variáveis da função exterior e parâmetros mjName ("Jackson"); //This celebrity is Michael Jackson
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:
function celebrityID () { var celebrityID = 999; //Nós retornamos um objeto com algumas funções interiores //Todas as funções interiores têm acesso as variáveis'da função exterior return { getID: function () { //Esta função interior vai retornar a variável celebrityID ATUALIZADA return celebrityID; }, setID: function (theNewId) { //Esta função interior vai mudar a variável da função exterior a qualquer hora celebrityID = theNewId; } } } var mjID = celebrityID (); //Nesta atual conjuntura, a função exterior celebrityID já retornou mjID.getID(); //999 mjID.setID(567); //Muda a variável da função exterior mjID.getID(); //567 - retorna o valor atualizado da variável celebrityID
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:
This post was last modified on 1 de maio de 2018 15:20