Qual è un uso pratico per una chiusura in JavaScript?


280

Sto cercando il mio più difficili da avvolgere la mia testa intorno chiusure JavaScript.

Ottengo che restituendo una funzione interna, avrà accesso a qualsiasi variabile definita nel suo genitore immediato.

Dove mi sarebbe utile? Forse non ho ancora capito bene. La maggior parte degli esempi che ho visto online non forniscono alcun codice del mondo reale, solo esempi vaghi.

Qualcuno può mostrarmi un uso reale di una chiusura?

È questo, per esempio?

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();

17
+1 per fare del tuo meglio :-) Le chiusure possono sembrare davvero scoraggianti per cominciare, so che erano per me. Una volta capito, sarai immediatamente un programmatore molto migliore.
Andy E

7
Ho appena scritto un post sul blog sulle chiusure in JavaScript che potresti trovare.
Skilldrick,

@Skilldrick. il link è morto ... e ho anche trovato questo esempio pratico molto utile. youtube.com/watch?v=w1s9PgtEoJs .
Abhi,

Risposte:


241

Ho usato le chiusure per fare cose come:

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

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

Come puoi vedere lì, aora è un oggetto, con un metodo publicfunction( a.publicfunction()) che chiama privatefunction, che esiste solo all'interno della chiusura. NON puoi chiamare privatefunctiondirettamente (cioè a.privatefunction()), solo publicfunction().

È un esempio minimo, ma forse riesci a vederne gli usi? Lo abbiamo usato per applicare metodi pubblici / privati.


26
Ah, se questa è una chiusura, allora ho usato chiusure senza saperlo! Metto spesso funzioni all'interno di un'altra come quella, e quindi espongo qualsiasi cosa di cui abbia bisogno pubblico restituendo un oggetto letterale come nel tuo esempio.
alex,

1
Sì, come vedi, mantieni il contesto della funzione perché l'oggetto che restituisci fa riferimento a variabili (e funzioni) al suo interno. Quindi li hai usati, semplicemente non lo sapevi.
Francisco Soto,

9
Tecnicamente ogni funzione che esegui in Javascript su un browser è una chiusura perché l'oggetto finestra è associato ad esso.
Adam Gent,

9
So che questa è una vecchia domanda, ma per me non fornisce ancora una risposta adeguata. Perché non chiamare semplicemente la funzione direttamente? Perché hai bisogno di una funzione privata?
qodeninja,

5
Perché anche se l'esempio ha solo una funzione, potrebbe anche contenere variabili non accessibili dall'esterno. Di ': var obj = (function () {var value = 0; return {get: function () {return value;}, set: function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 ecc.
Francisco Soto,

212

Supponiamo di voler contare il numero di volte in cui l'utente ha fatto clic su un pulsante in una pagina Web.
Per questo, stai attivando una funzione onclicksull'evento del pulsante per aggiornare il conteggio della variabile

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

Ora potrebbero esserci molti approcci come:

1) È possibile utilizzare una variabile globale e una funzione per aumentare il contatore :

var counter = 0;

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

Ma il problema è che qualsiasi script sulla pagina può cambiare il contatore, senza chiamareupdateClickCount() .


2) Ora, potresti pensare di dichiarare la variabile all'interno della funzione:

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

Ma hey! Ogni volta che updateClickCount()viene chiamata la funzione, il contatore viene nuovamente impostato su 1.


3) Stai pensando alle funzioni annidate ?

Le funzioni nidificate hanno accesso all'ambito "sopra" loro.
In questo esempio, la funzione interna updateClickCount()ha accesso alla variabile contatore nella funzione genitorecountWrapper()

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

Ciò avrebbe potuto risolvere il contro-dilemma, se avessi potuto raggiungere la updateClickCount()funzione dall'esterno e dovessi anche trovare un modo per eseguire counter = 0solo una volta non tutte le volte.


4) Chiusura in soccorso! (funzione auto-invocante) :

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

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

La funzione auto-invocante viene eseguita una sola volta. Imposta lo counterzero (0) e restituisce un'espressione di funzione.

In questo modo updateClickCountdiventa una funzione. La parte "meravigliosa" è che può accedere al contatore nell'ambito genitore.

Questa si chiama chiusura JavaScript . Consente a una funzione di avere variabili " private ".

Il counterè protetta dal campo di applicazione della funzione anonima, e può essere modificato solo con la funzione add!

Esempio più vivace sulla chiusura:

<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>


Riferimento: https://www.w3schools.com/js/js_function_closures.asp


50
Questa è la prima risposta che mi ha fatto dire "Oh, ecco perché avrei usato le chiusure!"
Devil's Advocate,

6
hai

15
Ho appena letto la pagina w3schools sulle chiusure e poi sono venuto qui per maggiori informazioni. Questo è lo stesso della pagina w3schools
tyelford

1
@JerryGoyal potresti farlo funzionare con 2 pulsanti separati? Non riesco a capire come senza ricorrere a 2 variabili (copie della funzione), che sembrano rimuovere alcuni dei principali vantaggi / praticità.
Tyler Collier,

2
Buona risposta. Nota che una chiusura non deve necessariamente essere una funzione auto-invocante, ma può esserlo. Quando una chiusura viene auto-invocata (cioè chiamata immediatamente aggiungendo () dopo la funzione), ciò significa che il valore restituito viene immediatamente calcolato, anziché la funzione restituita e il valore restituito calcolato successivamente una volta che la funzione viene invocata. Una chiusura può effettivamente essere qualsiasi funzione all'interno di un'altra funzione e la sua caratteristica chiave è che ha accesso all'ambito della funzione genitore, comprese le sue variabili e metodi.
Chris Halcrow,

69

L'esempio che dai è eccellente. Le chiusure sono un meccanismo di astrazione che ti consente di separare le preoccupazioni in modo molto pulito. Il tuo esempio è un caso di separazione della strumentazione (conteggio delle chiamate) dalla semantica (un'API per la segnalazione degli errori). Altri usi includono:

  1. Passare il comportamento parametrizzato in un algoritmo (programmazione classica di ordine superiore):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
  2. Simulazione di programmazione orientata agli oggetti:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
  3. Implementazione del controllo esotico del flusso, come la gestione degli eventi di jQuery e le API AJAX.


3
( int?) L'ultima volta che ho controllato, JavaScript era una lingua tipizzata da papera. Forse stavi pensando a Java?
Ciao,

1
@ Hello71: stavo pensando a JavaScript, ma le vecchie abitudini sono dure a morire. Buona pesca.
Marcelo Cantos,

2
@MarceloCantos sembra che tu abbia dimenticato una virgola nell'implementazione del contatore. Ho modificato il tuo post per correggerlo. Spero sia ok :)
Natan Streppel,

2
@Streppel: buona cattura! Sono più che felice che tu abbia migliorato il mio codice. :-)
Marcelo Cantos,

cercando di capire # 1 ... Come chiamer prossimita '?
Dave2081,

26

So di essere in ritardo nel rispondere a questa domanda, ma potrebbe aiutare chiunque cerchi ancora la risposta nel 2018.

Le chiusure Javascript possono essere utilizzate per implementare la funzionalità di accelerazione e debounce nell'applicazione.

Limitazione :

La limitazione pone un limite al numero massimo di volte in cui una funzione può essere richiamata nel tempo. Come in "eseguire questa funzione al massimo una volta ogni 100 millisecondi".

Codice :

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 :

Il debouncing pone un limite a una funzione che non può essere richiamata fino a quando non è trascorso un certo periodo di tempo senza che sia stata chiamata. Come in "eseguire questa funzione solo se sono trascorsi 100 millisecondi senza che sia stata chiamata."

Codice:

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

Come puoi vedere, le chiusure sono state di aiuto nell'implementazione di due meravigliose funzionalità che ogni app Web dovrebbe avere per offrire funzionalità di interfaccia utente fluida.

Spero che possa aiutare qualcuno.


18

Sì, questo è un buon esempio di utile chiusura. La chiamata a warnUser crea la calledCountvariabile nel suo ambito e restituisce una funzione anonima memorizzata nella warnForTampervariabile. Poiché è ancora presente una chiusura che utilizza la variabile calledCount, questa non viene eliminata all'uscita della funzione, quindi ogni chiamata a warnForTamper()aumenterà la variabile con ambito e avviserà il valore.

Il problema più comune che vedo su StackOverflow è quello in cui qualcuno vuole "ritardare" l'uso di una variabile che viene aumentata su ciascun ciclo, ma poiché la variabile ha un ambito, ogni riferimento alla variabile sarebbe dopo la fine del ciclo, con conseguente stato finale della variabile:

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

Ciò comporterebbe che ogni avviso mostrasse lo stesso valore di i, il valore a cui era aumentato alla fine del ciclo. La soluzione è creare una nuova chiusura, un ambito separato per la variabile. Questo può essere fatto usando una funzione anonima eseguita istantaneamente, che riceve la variabile e memorizza il suo stato come argomento:

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); 

Interessante -1, suppongo che questo non sia "un uso pratico per una chiusura in javascript"?
Andy E

1
Ho trovato utile leggerlo, quindi ho assegnato un +1 prima del voto negativo.
alex,

1
@alex: grazie, ho notato il voto. Mi sono quasi abituato ai voti anonimi qui a SO. Mi dà fastidio solo perché mi piacerebbe davvero sapere se ho detto qualcosa di inesatto o sbagliato, e tendono a farti pensare che sei appena stato retrocesso da qualche altro risponditore che vuole una migliore visibilità per la propria risposta. Fortunatamente, non sono un tipo vendicativo ;-)
Andy E

1
Penso che questo sia più un problema per l'ambito del blocco rotto JavaScripts. Dovresti solo essere in grado di aggiungere var j = i; prima del primo setTimeout e ricevi l'avviso per usare quel j. Un'altra soluzione è utilizzare 'with' in questo modo: for (var i = 0; i <someVar.length; i ++) {with ({i: i}) {window.setTimeout (function () {alert ("Value of ero "+ i +" quando è stato impostato questo timer ")}, 100);}}
davidbuttar,

1
@AndyE Funny potrebbe non essere la parola giusta. Ho appena notato che spesso le persone usano funzioni auto-invocanti per spiegare le chiusure, come molte risposte in questa pagina. Ma la funzione di callback in setTimeout è anche una chiusura; potrebbe essere considerato "un uso pratico" poiché è possibile accedere ad alcune altre variabili locali dal callback. Quando stavo imparando a conoscere le chiusure, rendermi conto che questo mi era utile - che le chiusure sono ovunque, non solo nei modelli arcade JavaScript.
antoine,

14

Nel linguaggio JavaScript (o qualsiasi altro ECMAScript), in particolare, le chiusure sono utili per nascondere l'implementazione della funzionalità pur rivelando l'interfaccia.

Ad esempio, immagina di scrivere una classe di metodi di utilità per la data e desideri consentire agli utenti di cercare i nomi dei giorni della settimana in base all'indice, ma non vuoi che siano in grado di modificare l'array di nomi che usi sotto il cofano.

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];
    };
  }())
};

Si noti che l' daysarray potrebbe semplicemente essere archiviato come proprietà dateUtildell'oggetto, ma sarebbe visibile agli utenti dello script e potrebbero anche modificarlo se lo desiderassero, senza nemmeno aver bisogno del codice sorgente. Tuttavia, poiché è racchiuso dalla funzione anonima che restituisce la funzione di ricerca della data, è accessibile solo dalla funzione di ricerca, quindi ora è a prova di manomissione.


2
Questo potrebbe sembrare stupido, ma non potrebbero semplicemente aprire il file JavaScript stesso e vedere la tua implementazione?
itsmichaelwang,

1
@Zapurdead: sì, ovviamente potevano vedere l'implementazione ma non potevano cambiarla (accidentalmente o intenzionalmente) senza modificare direttamente il codice sorgente. Suppongo che potresti confrontarlo con membri protetti in Java.
Maerics,


5

Un altro uso comune delle chiusure è quello di associare thisun metodo a un oggetto specifico, consentendogli di essere chiamato altrove (come un gestore di eventi).

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);

Ogni volta che viene generato un evento mousemove, watcher.follow(evt)viene chiamato.

Le chiusure sono anche una parte essenziale delle funzioni di ordine superiore, consentendo il modello molto comune di riscrivere più funzioni simili come un'unica funzione di ordine superiore parametrizzando le porzioni diverse. Come esempio astratto,

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

diventa

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

dove A e B non sono unità sintattiche ma stringhe di codice sorgente (non valori letterali di stringa).

Vedi " Ottimizzare il mio javascript con una funzione " per un esempio concreto.


5

Qui ho un saluto che voglio dire più volte. Se creo una chiusura, posso semplicemente chiamare quella funzione per registrare il saluto. Se non creo la chiusura, devo passare il mio nome ogni volta.

Senza chiusura ( 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");

Con una chiusura ( 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();

1
Non sono sicuro ma ancora senza una chiusura puoi chiamare come var grretBilly = saluto ("Billy", "Bob"); e chiama grretBilly (); Stilll farebbe lo stesso ?? anche se crei la chiusura o no, questo è un problema diverso, ma passare il nome ogni volta non è un problema qui.
user2906608

4

Se ti senti a tuo agio con il concetto di creare un'istanza di una classe nel senso orientato agli oggetti (cioè creare un oggetto di quella classe), allora sei vicino alla comprensione delle chiusure.

Pensala in questo modo: quando crei un'istanza di due oggetti Person, sai che la variabile "Name" del membro della classe non è condivisa tra istanze; ogni oggetto ha la sua 'copia'. Allo stesso modo, quando si crea una chiusura, la variabile libera ("chiamato Conte" nell'esempio sopra) viene associata all'istanza della funzione.

Penso che il tuo salto concettuale sia leggermente ostacolato dal fatto che ogni funzione / chiusura restituita dalla funzione warnUser (a parte: questa è una funzione di ordine superiore ) lega 'chiamatoCount' con lo stesso valore iniziale (0), mentre spesso quando si creano chiusure è più utile passare diversi inizializzatori nella funzione di ordine superiore, proprio come passare valori diversi al costruttore di una classe.

Quindi, supponiamo che quando 'calledCount' raggiunga un determinato valore che desideri terminare la sessione dell'utente; potresti volere valori diversi per questo a seconda che la richiesta arrivi dalla rete locale o dalla grande e cattiva Internet (sì, è un esempio inventato). Per raggiungere questo obiettivo, è possibile passare diversi valori iniziali per calledCount in warnUser (ovvero -3 o 0?).

Parte del problema con la letteratura è la nomenclatura usata per descriverli ("ambito lessicale", "variabili libere"). Non lasciarti ingannare, le chiusure sono più semplici di quanto sembrerebbero ... prima facie ;-)


3

Qui ho un semplice esempio di concetto di chiusura che possiamo usare nel nostro sito di e-commerce o anche in molti altri. Sto aggiungendo il mio link jsfiddle con l'esempio. contiene un piccolo elenco di prodotti composto da 3 articoli e un contatore carrello.

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>


2

Uso di chiusure:

Le chiusure sono una delle funzionalità più potenti di JavaScript. JavaScript consente l'annidamento di funzioni e garantisce alla funzione interna pieno accesso a tutte le variabili e funzioni definite all'interno della funzione esterna (e tutte le altre variabili e funzioni a cui la funzione esterna ha accesso). Tuttavia, la funzione esterna non ha accesso alle variabili e alle funzioni definite all'interno della funzione interna. Ciò fornisce una sorta di sicurezza per le variabili della funzione interna. Inoltre, poiché la funzione interna ha accesso all'ambito della funzione esterna, le variabili e le funzioni definite nella funzione esterna vivranno più a lungo della funzione esterna stessa, se la funzione interna riesce a sopravvivere oltre la durata della funzione esterna.

Esempio :

<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>

Nel codice sopra, la variabile name della funzione esterna è accessibile alle funzioni interne e non c'è altro modo per accedere alle variabili interne se non attraverso le funzioni interne. Le variabili interne della funzione interna fungono da depositi sicuri per le funzioni interne. Conservano dati "persistenti", ma sicuri, per le funzioni interne con cui lavorare. Le funzioni non devono nemmeno essere assegnate a una variabile o avere un nome. leggi qui per i dettagli


2

Mi piace l' esempio di factory di funzioni di Mozilla .

function makeAdder(x) {

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

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

11
Questo è il tipo di esempio che, a mio avviso, non aiuta le persone a capire chiusure o a cosa servono. Quante volte hai mai scritto una chiusura per restituire una funzione per aggiungere numeri, se non come esempio?
Mohamad,

2

Il modello del modulo JavaScript utilizza le chiusure. Il suo bel disegno ti permette di avere qualcosa di simile "pubblico" e "privato".

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 );

    }
  };

})();


1

Questo thread mi ha aiutato immensamente a comprendere meglio come funzionano le chiusure. Da allora ho fatto una mia sperimentazione e ho escogitato questo codice abbastanza semplice che può aiutare alcune altre persone a vedere come le chiusure possono essere utilizzate in modo pratico e come utilizzare la chiusura a diversi livelli per mantenere variabili simili a statiche e / o variabili globali senza il rischio che vengano sovrascritte o confuse con le variabili globali. Ciò che fa è tenere traccia dei clic sui pulsanti, sia a livello locale per ogni singolo pulsante sia a livello globale, contando ogni clic di pulsante e contribuendo a una singola cifra. Nota non ho usato alcuna variabile globale per fare questo, che è una specie del punto dell'esercizio: avere un gestore che può essere applicato a qualsiasi pulsante che contribuisce anche a qualcosa a livello globale.

Per favore, esperti, fatemi sapere se ho commesso delle cattive pratiche qui! Sto ancora imparando queste cose da solo.

<!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>

0

Riferimento: utilizzo pratico delle chiusure

In pratica le chiusure possono creare disegni eleganti, che consentono la personalizzazione di vari calcoli, chiamate differite, richiamate, creazione di ambito incapsulato ecc.

Un esempio del metodo di ordinamento delle matrici che accetta come argomento la funzione di ordinamento-condizione:

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

Mappatura dei funzionali come metodo di mappatura degli array che mappa un nuovo array in base alla condizione dell'argomento funzionale:

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

Spesso è conveniente implementare le funzioni di ricerca utilizzando argomenti funzionali che definiscono condizioni quasi illimitate per la ricerca:

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

Inoltre, possiamo notare l'applicazione di funzionali come, ad esempio, un metodo forEach che applica una funzione a una matrice di elementi:

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

Una funzione viene applicata agli argomenti (a un elenco di argomenti - in applica e agli argomenti posizionati - in chiamata):

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

Chiamate differite:

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

Funzioni di richiamata:

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
};

Creazione di un ambito incapsulato allo scopo di nascondere oggetti ausiliari:

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

0

Gran parte del codice che scriviamo in JavaScript front-end è basato su eventi: definiamo alcuni comportamenti, quindi li alleghiamo a un evento che viene attivato dall'utente (come un clic o un tasto premuto). Il nostro codice è generalmente allegato come callback: una singola funzione che viene eseguita in risposta all'evento. size12, size14 e size16 sono ora funzioni che ridimensioneranno il corpo del testo rispettivamente a 12, 14 e 16 pixel. Possiamo collegarli ai pulsanti (in questo caso i collegamenti) come segue:

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


Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo riguardo a come e / o perché risolve il problema migliorerebbe il valore a lungo termine della risposta.
Donald Duck

1
questo esempio, mi sembra, potrebbe essere implementato senza chiusura tramite una funzione standard. Sto cercando di trovare un esempio di qualcosa che NON POTREBBE essere implementato senza chiusura
Zach Smith,

0

Le chiusure sono un modo utile per creare , una sequenza incrementata su richiesta:

    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

Le differenze sono riassunte come segue:

Funzioni anonime Funzioni definite

Non può essere usato come metodo Può essere usato come metodo di un oggetto

Esiste solo nell'ambito in cui è definito Esiste all'interno dell'oggetto in cui è definito

Può essere chiamato solo nell'ambito in cui è definito Può essere chiamato in qualsiasi punto del codice

Può essere riassegnato un nuovo valore o eliminato Non può essere eliminato o modificato

Riferimenti


0

Nel campione dato il valore della variabile racchiusa "contatore" è protetto e può essere modificato solo usando le funzioni fornite (incremento, decremento). perché è in una chiusura,

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


0

Spiegare l'uso pratico di una chiusura in JavaScript ---

Quando creiamo una funzione all'interno di un'altra funzione, stiamo creando una chiusura. Le chiusure sono potenti perché sono in grado di leggere e manipolare i dati delle sue funzioni esterne. Ogni volta che viene invocata una funzione, viene creato un nuovo ambito per quella chiamata. Le variabili locali dichiarate all'interno della funzione appartengono a quell'ambito e sono accessibili solo da quella funzione. Quando la funzione ha terminato l'esecuzione, l'ambito viene generalmente distrutto.

Un semplice esempio di tale funzione è questo:

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

Nell'esempio sopra, la funzione buildName () dichiara un saluto della variabile locale e lo restituisce. Ogni chiamata di funzione crea un nuovo ambito con una nuova variabile locale. Al termine dell'esecuzione della funzione, non abbiamo modo di fare nuovamente riferimento a tale ambito, quindi vengono raccolti i rifiuti.

Ma che dire di quando abbiamo un collegamento a tale ambito?

Diamo un'occhiata alla prossima funzione:

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

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

La funzione sayName () di questo esempio è una chiusura. La funzione sayName () ha il suo ambito locale (con benvenuto variabile) e ha anche accesso all'ambito della funzione esterna (che racchiude). In questo caso, il saluto variabile da buildName ().

Al termine dell'esecuzione di buildName, in questo caso l'ambito non viene distrutto. La funzione sayMyName () ha ancora accesso ad essa, quindi non verrà raccolta in modo inutile. Tuttavia, non esiste altro modo per accedere ai dati dall'ambito esterno tranne la chiusura. La chiusura funge da gateway tra il contesto globale e l'ambito esterno.


0

Sto cercando di imparare chiusure e penso che l'esempio che ho creato sia un caso d'uso pratico. Puoi eseguire lo snippet e vedere il risultato nella console. abbiamo due utenti separati che hanno dati separati. Ognuno di loro può vedere lo stato attuale e aggiornarlo.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.