Qual é o uso prático para um fechamento em JavaScript?

279

Estou tentando ao máximo para entender os fechamentos de JavaScript.

Eu entendo que, retornando uma função interna, ela terá acesso a qualquer variável definida em seu pai imediato.

Onde isso seria útil para mim? Talvez eu ainda não tenha pensado nisso. A maioria dos exemplos que eu vi online não fornece nenhum código do mundo real, apenas exemplos vagos.

Alguém pode me mostrar um uso real de um fechamento?

É este, por exemplo?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();
alex
fonte
17
+1 por tentar o seu melhor :-) Os fechamentos podem parecer realmente assustadores para começar, eu sei que eles eram para mim. Depois de pegar o jeito, você instantaneamente será um codificador muito melhor.
Andy E
7
Acabei de escrever uma postagem no blog sobre fechamentos em JavaScript que você pode encontrar helfpul.
Skilldrick
@Skilldrick. link está morto ... e também achei este exemplo prático muito útil. youtube.com/watch?v=w1s9PgtEoJs .
Abhi

Respostas:

240

Eu usei fechamentos para fazer coisas como:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

Como você pode ver lá, aagora é um objeto, com um método publicfunction( a.publicfunction()) que chama privatefunction, que só existe dentro do fechamento. Você NÃO pode ligar privatefunctiondiretamente (ie a.privatefunction()), apenas publicfunction().

É um exemplo mínimo, mas talvez você possa ver usos para ele? Usamos isso para aplicar métodos públicos / privados.

Francisco Soto
fonte
26
Ah, se isso é um fechamento, então eu usei fechamentos sem saber! Costumo colocar funções dentro de outra assim e expor todas as que necessito de público, retornando um objeto literal como no seu exemplo.
28410 alex
1
Sim, como você vê, você mantém o contexto da função porque o objeto retornado faz referência a variáveis ​​(e funções) dentro dele. Então você os usa, simplesmente não sabia.
Francisco Soto
9
Tecnicamente, todas as funções que você faz em Javascript em um navegador são fechadas porque o objeto da janela está vinculado a ele.
Adam Gent
9
Sei que essa é uma pergunta antiga, mas para mim isso ainda não fornece uma resposta adequada. Por que não chamar a função diretamente? Por que você precisa de uma função privada?
Qodeninja
5
Porque, embora o exemplo tenha apenas uma função, ele também pode ter variáveis ​​que não são acessíveis externamente. Dizer: var obj = (função () {valor var = 0; retornar {obter: função () {valor retornar;}, definir: função (val) {valor = val;}}}) (); obj.set (20); obj.get (); => 20 etc.
Francisco Soto
210

Suponha que você queira contar o número de vezes que o usuário clicou em um botão em uma página da web.
Para isso, você está acionando uma função no onclickevento do botão para atualizar a contagem da variável

<button onclick="updateClickCount()">click me</button>  

Agora, pode haver muitas abordagens como:

1) Você pode usar uma variável global e uma função para aumentar o contador :

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

Mas, a armadilha é que qualquer script na página pode alterar o contador, sem chamarupdateClickCount() .


2) Agora, você pode estar pensando em declarar a variável dentro da função:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

Mas, ei! Cada vez que a updateClickCount()função é chamada, o contador é definido como 1 novamente.


3) Pensando em funções aninhadas ?

Funções aninhadas têm acesso ao escopo "acima" delas.
Neste exemplo, a função interna updateClickCount()tem acesso à variável de contador na função paicountWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

Isso poderia ter resolvido o dilema do contador, se você pudesse alcançar a updateClickCount()função de fora e também precisar encontrar uma maneira de executar counter = 0apenas uma vez, nem sempre.


4) Fechamento para o resgate! (função de chamada automática) :

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

A função de auto-chamada é executada apenas uma vez. Ele define counterzero (0) e retorna uma expressão de função.

Desta forma, updateClickCounttorna-se uma função. A parte "maravilhosa" é que ele pode acessar o contador no escopo pai.

Isso é chamado de fechamento do JavaScript . Permite que uma função tenha " private variáveis ​​" ".

O counteré protegido pelo escopo da função anônima e só pode ser alterado usando a função add!

Exemplo mais animado sobre o encerramento:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


Referência: https://www.w3schools.com/js/js_function_closures.asp

JerryGoyal
fonte
49
Esta é a primeira resposta que me fez dizer "Oh, é por isso que eu usaria fechos!"
Advogado do Diabo
6
u fez o meu dia :)
JerryGoyal
15
Acabei de ler a página do w3schools sobre fechamentos e depois vim aqui para obter mais informações. Este é o mesmo que a página w3schools: w3schools.com/js/js_function_closures.asp
tyelford
1
@JerryGoyal você poderia fazê-lo funcionar com 2 botões separados? Não consigo descobrir como, sem recorrer a duas variáveis ​​(cópias da função), o que parece remover alguns dos principais benefícios / conveniência.
precisa saber é o seguinte
2
Boa resposta. Observe, porém, que um fechamento não precisa ser uma função de auto-chamada, mas pode ser. Quando um fechamento é chamado automaticamente (ou seja, chamado imediatamente adicionando () após a função), isso significa que o valor de retorno é calculado imediatamente, em vez da função que está sendo retornada e o valor de retorno calculado mais tarde, uma vez que a função é invocada. Um fechamento pode realmente ser qualquer função dentro de outra função, e sua principal característica é que ele tem acesso ao escopo da função pai, incluindo suas variáveis ​​e métodos.
22618 Chris Halcrow
69

O exemplo que você dá é excelente. Os fechamentos são um mecanismo de abstração que permite separar as preocupações de maneira muito limpa. Seu exemplo é um caso de separação de instrumentação (contagem de chamadas) da semântica (uma API de relatório de erros). Outros usos incluem:

  1. Passando o comportamento parametrizado para um algoritmo (programação clássica de ordem superior):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
  2. Simulando programação orientada a objetos:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
  3. Implementando controle de fluxo exótico, como manipulação de eventos do jQuery e APIs AJAX.

Marcelo Cantos
fonte
3
( int?) A última vez que verifiquei, o JavaScript era uma linguagem tipada por pato. Talvez você estivesse pensando em Java?
Hello71
1
@ Hello71: Eu estava pensando em JavaScript, mas os velhos hábitos morrem muito. Boa pegada.
Marcelo Cantos
2
@ MarceloCantos parece que você esqueceu uma vírgula na implementação do contador. Eu editei sua postagem para corrigi-la. Esperança de que é ok :)
Natan Streppel
2
@Streppel: Boa captura! Estou mais do que feliz por você melhorar meu código. :-)
Marcelo Cantos
tentando entender o número 1 ... Como você chamaria a proximidade_sort?
Dave2081
26

Sei que estou super atrasado em responder a essa pergunta, mas isso pode ajudar quem ainda procura a resposta em 2018.

Os fechamentos Javascript podem ser usados ​​para implementar a aceleração e o rebote funcionalidade de em seu aplicativo.

Limitação :

A limitação limita o número máximo de vezes que uma função pode ser chamada ao longo do tempo. Como em "execute esta função no máximo uma vez a cada 100 milissegundos".

Código:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

Debouncing :

A conversão coloca um limite em uma função que não será chamada novamente até que um certo período de tempo tenha passado sem que ela seja chamada. Como em "execute esta função somente se 100 milissegundos se passaram sem que ela seja chamada".

Código:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

Como você pode ver, os fechamentos ajudaram na implementação de dois belos recursos que todo aplicativo da Web deve ter para fornecer uma funcionalidade suave da experiência da interface do usuário.

Espero que ajude alguém.

Mohd Asim Suhail
fonte
18

Sim, esse é um bom exemplo de fechamento útil. A chamada para warnUser cria a calledCountvariável em seu escopo e retorna uma função anônima que é armazenada na warnForTampervariável. Como ainda existe um fechamento usando a variável chamadaCount, ela não é excluída na saída da função, portanto, cada chamada para owarnForTamper() aumentará a variável com escopo e alertará o valor.

O problema mais comum que vejo no StackOverflow é onde alguém quer "atrasar" o uso de uma variável que é aumentada em cada loop, mas como a variável tem escopo definido, cada referência à variável seria após o encerramento do loop, resultando no estado final da variável:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

Isso resultaria em todos os alertas mostrando o mesmo valor de i, o valor para o qual foi aumentado quando o loop terminou. A solução é criar um novo fechamento, um escopo separado para a variável. Isso pode ser feito usando uma função anônima executada instantaneamente, que recebe a variável e armazena seu estado como argumento:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 
Andy E
fonte
-1 Interessante, acho que esse não é "um uso prático para um fechamento em javascript"?
Andy E
1
Eu encontrei alguma utilidade em lê-lo, então concordei com um +1 antes da votação.
alex
1
@ alex: obrigado, eu notei o voto positivo. Eu quase me acostumei a votos anônimos aqui na SO. Isso só me incomoda, porque eu realmente gostaria de saber se eu disse algo impreciso ou errado, e eles tendem a fazer você pensar que acabou de ser votado por algum outro atendente que deseja uma melhor visibilidade para sua própria resposta. Felizmente, eu não sou o tipo vingativo ;-)
Andy E
1
Eu acho que isso é mais uma solução para o escopo de bloco quebrado JavaScripts. Você deve ser capaz de adicionar var j = i; antes do primeiro setTimeout e obtenha o alerta para usar esse j. Outra solução é usar 'with' assim: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Value of i era "+ i +" quando este temporizador foi definido")}, 100);}}
davidbuttar
1
@ Andy Engraçado pode não ser a palavra certa. Acabei de notar que, muitas vezes, as pessoas usam funções de auto-chamada para explicar fechamentos, como muitas respostas nesta página. Mas a função de retorno de chamada no setTimeout também é um fechamento; pode ser considerado "um uso prático", pois você pode acessar algumas outras variáveis ​​locais a partir do retorno de chamada. Quando eu estava aprendendo sobre fechamentos, percebi que isso era útil para mim - que os fechamentos estão em toda parte, não apenas nos padrões JavaScript de arcade.
Antoine
14

Na linguagem JavaScript (ou em qualquer ECMAScript), em particular, os fechamentos são úteis para ocultar a implementação da funcionalidade e, ao mesmo tempo, revelar a interface.

Por exemplo, imagine que você está escrevendo uma classe de métodos utilitários de data e deseja permitir que os usuários pesquisem nomes de dias da semana por índice, mas não deseja que eles possam modificar a matriz de nomes que você usa sob o capô.

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

Observe que a daysmatriz poderia simplesmente ser armazenada como uma propriedade do dateUtilobjeto, mas seria visível para os usuários do script e eles poderiam até alterá-lo se quisessem, sem precisar do seu código-fonte. No entanto, como é delimitada pela função anônima que retorna a função de pesquisa de data, ela só pode ser acessada pela função de pesquisa e, portanto, é à prova de violação.

maerics
fonte
2
Isso pode parecer idiota, mas eles não poderiam simplesmente abrir o próprio arquivo JavaScript e ver sua implementação?
itsmichaelwang
1
@ Zapurdead: sim, é claro que eles puderam ver a implementação, mas não puderam alterar a implementação (acidental ou intencionalmente) sem modificar diretamente seu código-fonte. Suponho que você possa compará-lo com membros protegidos em Java.
maerics
6

Há uma seção sobre fechamentos práticos na Mozilla Developer Network .

alex
fonte
Examinando isso, não vejo como é "prático" como se eu removesse o conjunto, return function ()...o código ainda funciona bem. O fechamento não é necessário
uma brincadeira
@ James_Parsons Você não pode atribuí-los a manipuladores de eventos, como fizeram no exemplo então.
187 alex
5

Outro uso comum para fechamentos é vincular thisum método a um objeto específico, permitindo que ele seja chamado em outro lugar (como um manipulador de eventos).

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

Sempre que um evento de remoção de mouse é acionado, watcher.follow(evt) é é chamado.

Os fechamentos também são uma parte essencial das funções de ordem superior, permitindo o padrão muito comum de reescrever várias funções semelhantes como uma única função de ordem superior, parametrizando as partes diferentes. Como um exemplo abstrato,

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

torna-se

fooer = function (x) {
    return function (...) {A x B}
}

onde A e B não são unidades sintáticas, mas cadeias de código-fonte (não literais).

Consulte " Otimizando meu javascript com uma função " para um exemplo concreto.

fora
fonte
5

Aqui, tenho uma saudação que quero dizer várias vezes. Se eu criar um fechamento, posso simplesmente chamar essa função para gravar a saudação. Se eu não criar o fechamento, tenho que passar meu nome sempre.

Sem fechamento ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

Com um fechamento ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();
Luke Schlangen
fonte
1
Não tenho certeza, mas ainda sem um fechamento, você pode chamar como var grretBilly = saudação ("Billy", "Bob"); e chame grretBilly (); Ainda faria o mesmo? embora você crie um fechamento ou não, esse é um problema diferente, mas a passagem de nome toda vez não é um problema aqui.
User2906608
4

Se você se sente confortável com o conceito de instanciar uma classe no sentido orientado a objetos (por exemplo, para criar um objeto dessa classe), está próximo de entender os fechamentos.

Pense dessa maneira: quando você instancia dois objetos Person, sabe que a variável de membro da classe "Name" não é compartilhada entre instâncias; cada objeto tem sua própria 'cópia'. Da mesma forma, quando você cria um fechamento, a variável livre ('chamadaCount' no seu exemplo acima) é vinculada à 'instância' da função.

Eu acho que seu salto conceitual é um pouco dificultado pelo fato de que todas as funções / fechamentos retornados pela função warnUser (exceto: essa é uma função de ordem superior ) vinculam 'namedCount' com o mesmo valor inicial (0), embora muitas vezes ao criar closures é mais útil passar inicializadores diferentes para a função de ordem superior, como passar valores diferentes para o construtor de uma classe.

Portanto, suponha que quando 'namedCount' atingir um determinado valor que você deseja encerrar a sessão do usuário; você pode querer valores diferentes para isso, dependendo se a solicitação vem da rede local ou da grande e ruim Internet (sim, é um exemplo artificial). Para conseguir isso, você pode passar diferentes valores iniciais para chamado Count para warnUser (ou seja, -3 ou 0?).

Parte do problema da literatura é a nomenclatura usada para descrevê-los ("escopo lexical", "variáveis ​​livres"). Não se deixe enganar, os fechamentos são mais simples do que pareceriam ... prima facie ;-)

EdwardGarson
fonte
3

Aqui eu tenho um exemplo simples de conceito de fechamento que podemos usar em nosso site de comércio eletrônico ou em muitos outros também. Estou adicionando meu link jsfiddle com o exemplo. Ele contém uma pequena lista de produtos com 3 itens e um contador de carrinho.

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>

Abhilash
fonte
2

Uso de Fechos:

Os fechamentos são um dos recursos mais poderosos do JavaScript. O JavaScript permite o aninhamento de funções e concede à função interna acesso total a todas as variáveis ​​e funções definidas dentro da função externa (e a todas as outras variáveis ​​e funções às quais a função externa tem acesso). No entanto, a função externa não tem acesso às variáveis ​​e funções definidas dentro da função interna. Isso fornece um tipo de segurança para as variáveis ​​da função interna. Além disso, como a função interna tem acesso ao escopo da função externa, as variáveis ​​e funções definidas na função externa viverão mais tempo que a própria função externa, se a função interna conseguir sobreviver além da vida útil da função externa.

Exemplo:

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

No código acima, a variável name da função externa é acessível para as funções internas e não há outra maneira de acessar as variáveis ​​internas, exceto através das funções internas. As variáveis ​​internas da função interna atuam como reservas seguras para as funções internas. Eles mantêm dados "persistentes", mas seguros, para as funções internas trabalharem. As funções nem precisam ser atribuídas a uma variável ou ter um nome. leia aqui para detalhes

Sunny SM
fonte
2

Eu gosto do exemplo da fábrica de funções do Mozilla .

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);
Tom
fonte
11
Este é o tipo de exemplo que não ajuda as pessoas a entender os fechamentos ou para que servem, na minha opinião. Quantas vezes você já escreveu um fechamento para retornar uma função para adicionar números, exceto como exemplo?
Mohamad
2

O padrão do módulo JavaScript usa fechamentos. Seu bom padrão permite que você tenha algo parecido com "público" e "privado".

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();
Tomasz Grabowski
fonte
1

Esse tópico me ajudou imensamente a entender melhor como funcionam os fechamentos. Desde então, fiz algumas experiências e criei esse código bastante simples que pode ajudar outras pessoas a ver como os fechamentos podem ser usados ​​de maneira prática e como usar o fechamento em diferentes níveis para manter variáveis ​​semelhantes a estática e / ou variáveis ​​globais sem risco de serem substituídos ou confundidos com variáveis ​​globais. O que isso faz é acompanhar os cliques no botão, tanto no nível local de cada botão individual quanto no nível global, contando cada clique do botão, contribuindo para uma única figura. Observe que não usei nenhuma variável global para fazer isso, que é o objetivo do exercício - ter um manipulador que possa ser aplicado a qualquer botão que também contribua para algo globalmente.

Por favor, especialistas, deixe-me saber se eu cometi alguma má prática aqui! Eu ainda estou aprendendo essas coisas.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>
Darren Crabb
fonte
0

Referência: Uso prático de tampas

Na prática, os fechamentos podem criar designs elegantes, permitindo a personalização de vários cálculos, chamadas adiadas, retornos de chamada, criação de escopo encapsulado etc.

Um exemplo do método de classificação de matrizes que aceita como argumento a função de condição de classificação:

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

Mapeando funcionais como o método map de matrizes que mapeia uma nova matriz pela condição do argumento funcional:

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

Muitas vezes, é conveniente implementar funções de pesquisa usando argumentos funcionais que definem condições quase ilimitadas para a pesquisa:

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

Além disso, podemos observar a aplicação de funcionais como, por exemplo, um método forEach que aplica uma função a uma matriz de elementos:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

Uma função é aplicada aos argumentos (a uma lista de argumentos - em aplicar e a argumentos posicionados - em chamada):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

Chamadas diferidas:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

Funções de retorno de chamada:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

Criação de um escopo encapsulado com a finalidade de ocultar objetos auxiliares:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10
Damodaran
fonte
0

Grande parte do código que escrevemos no JavaScript front-end é baseado em eventos - definimos algum comportamento e o anexamos a um evento que é disparado pelo usuário (como um clique ou um pressionamento de tecla). Nosso código geralmente é anexado como um retorno de chamada: uma única função que é executada em resposta ao evento. size12, size14 e size16 agora são funções que redimensionam o texto do corpo para 12, 14 e 16 pixels, respectivamente. Podemos anexá-los a botões (neste caso, links) da seguinte maneira:

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

Violino

Muhammad Usman
fonte
Embora esse código possa responder à pergunta, fornecer um contexto adicional sobre como e / ou por que resolve o problema melhoraria o valor a longo prazo da resposta.
Donald Duck
1
este exemplo, parece-me, poderia ser implementado sem fechamento por meio de uma função padrão. Estou tentando encontrar um exemplo de algo que não poderia ser implementada sem fechamento
Zach Smith
0

Os fechamentos são uma maneira útil de criar , uma sequência incrementada sob demanda:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

As diferenças estão resumidas da seguinte forma:

Funções anônimas Funções definidas

Não pode ser usado como um método Pode ser usado como um método de um objeto

Existe apenas no escopo em que está definido. Existe dentro do objeto em que está definido.

Só pode ser chamado no escopo em que está definido Pode ser chamado em qualquer ponto do código

Pode ser reatribuído um novo valor ou excluído Não pode ser excluído ou alterado

Referências

Paul Sweatte
fonte
0

Na amostra fornecida, o valor da variável fechada 'counter' é protegido e pode ser alterado apenas usando as funções fornecidas (incremento, decremento). porque está em um fechamento,

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2

ShAkKiR
fonte
0

Explicando o uso prático de um Encerramento em JavaScript ---

Quando criamos uma função dentro de outra função, estamos criando um fechamento. Os fechamentos são poderosos porque são capazes de ler e manipular os dados de suas funções externas. Sempre que uma função é chamada, um novo escopo é criado para essa chamada. A variável local declarada dentro da função pertence a esse escopo e só pode ser acessada a partir dessa função. Quando a função termina a execução, o escopo geralmente é destruído.

Um exemplo simples de tal função é este:

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

No exemplo acima, a função buildName () declara uma saudação de variável local e a retorna. Cada chamada de função cria um novo escopo com uma nova variável local. Depois que a função é executada, não temos como nos referir a esse escopo novamente, portanto, é coletado como lixo.

Mas e quando temos um link para esse escopo?

Vejamos a próxima função:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

A função sayName () deste exemplo é um fechamento. A função sayName () possui seu próprio escopo local (com variável bem-vinda) e também tem acesso ao escopo da função externa (anexa). Nesse caso, a saudação variável de buildName ().

Após a execução do buildName, o escopo não é destruído nesse caso. A função sayMyName () ainda tem acesso a ela, portanto não será coletada como lixo. No entanto, não há outra maneira de acessar dados do escopo externo, exceto o fechamento. O fechamento serve como porta de entrada entre o contexto global e o escopo externo.

Mandeep Kaur
fonte
0

Estou tentando aprender clousures e acho que o exemplo que criei é um caso de uso prático. Você pode executar o snippet e ver o resultado no console. temos dois usuários separados que possuem dados separados. Cada um deles pode ver o estado real e atualizá-lo.

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

Krystian Wójcicki
fonte