86 Domanda: Come funzionano le chiusure JavaScript?

domanda creata a Sun, Apr 9, 2017 12:00 AM

Come spiegheresti le chiusure di JavaScript a qualcuno con una conoscenza dei concetti in cui consistono (ad esempio funzioni, variabili e simili), ma non capisce le chiusure stesse?

Ho visto l'esempio Scheme fornito su Wikipedia, ma sfortunatamente lo ha fatto non aiutare.

    
7649
  1. Il mio problema con queste e molte risposte è che si avvicinano da una prospettiva astratta, teorica, piuttosto che iniziare spiegando semplicemente perché le chiusure sono necessarie in Javascript e nelle situazioni pratiche in cui li usi Finisci con un articolo che devi sfondare, sempre pensando: "ma perché?". Vorrei iniziare semplicemente con: le chiusure sono un modo pulito di trattare le seguenti due realtà di JavaScript: a. l'ambito è a livello di funzione, non a livello di blocco e, b. gran parte di ciò che fai in pratica in JavaScript è asincrono /basato su eventi.
    2013-03-08 17: 22: 59Z
  2. @ Redsandro Per uno, rende il codice basato sugli eventi molto più facile da scrivere. Potrei attivare una funzione quando la pagina viene caricata per determinare le specifiche relative all'HTML o alle funzionalità disponibili. Posso definire e impostare un gestore in quella funzione e avere tutte le informazioni di contesto disponibili ogni volta che viene chiamato il gestore senza doverlo interrogare nuovamente. Risolvi il problema una volta, riutilizzalo in ogni pagina in cui è necessario quel gestore con un sovraccarico ridotto sul richiamo del gestore. Hai mai visto gli stessi dati essere ri-mappati due volte in una lingua che non li ha? Le chiusure rendono molto più semplice evitare questo tipo di cose.
    2013-06-26 17: 02: 16Z
  3. Per i programmatori Java, la risposta breve è che è la funzione equivalente di una classe interna. Una classe interna contiene anche un puntatore implicito su un'istanza della classe esterna e viene utilizzata per lo stesso scopo (ovvero, la creazione di gestori di eventi).
    2014-06-19 10: 04: 21Z
  4. Ho capito molto meglio da qui: javascriptissexy.com/understand-javascript-closures-with-ease . Aveva ancora bisogno di una chiusura alla chiusura dopo aver letto le altre risposte. :)
    2016-01-22 05: 41: 32Z
  5. Ho trovato questo esempio pratico molto utile: youtube.com/watch?v=w1s9PgtEoJs
    2016-07-06 17: 33: 55Z
30 risposte                              30                         

Chiusure JavaScript per principianti

Inviato da Morris il martedì, 21.02.2006 10:19. Pubblicato dalla community.

Le chiusure non sono magiche

Questa pagina spiega le chiusure in modo che un programmatore possa capirle - usando il codice JavaScript funzionante. Non è per guru o programmatori funzionali.

Le chiusure sono non difficili per capire una volta che il concetto di base è bannato. Tuttavia, è impossibile capire leggendo qualsiasi spiegazione teorica o accademica!

Questo articolo è destinato ai programmatori con esperienza di programmazione in un linguaggio tradizionale e che possono leggere la seguente funzione JavaScript:

 
function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');

Due brevi riepiloghi

  • Quando una funzione (foo) dichiara altre funzioni (bar e baz), la famiglia di variabili locali create in foo è non distrutta ​​em> quando la funzione viene chiusa. Le variabili diventano semplicemente invisibili al mondo esterno. foo può quindi abilmente restituire le funzioni bar e baz e possono continuare a leggere, scrivere e comunicare tra lororuvida questa famiglia di variabili chiuse ("la chiusura") che nessun altro può intromettersi, nemmeno qualcuno che richiama il foo in futuro.

  • Una chiusura è un modo per supportare funzioni di prima classe ; è un'espressione che può fare riferimento a variabili all'interno del suo ambito (quando è stato dichiarato per la prima volta), essere assegnato a una variabile, essere passato come argomento a una funzione, o essere restituito come risultato di una funzione.

Un esempio di chiusura

Il seguente codice restituisce un riferimento a una funzione:

 
function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

La maggior parte dei programmatori JavaScript comprenderà come un riferimento a una funzione viene restituito a una variabile (say2) nel codice precedente. Se non lo fai, allora devi guardarlo prima di poter imparare chiusure. Un programmatore che usa C penserebbe che la funzione restituisca un puntatore a una funzione e che le variabili say e say2 rappresentino ciascuna un puntatore a una funzione.

C'è una differenza fondamentale tra un puntatore C in una funzione e un riferimento JavaScript in una funzione. In JavaScript, puoi pensare a una variabile di riferimento di funzione come avere sia un puntatore a una funzione che come un puntatore nascosto a una chiusura.

Il codice precedente ha una chiusura perché la funzione anonima function() { console.log(text); } è dichiarata dentro un'altra funzione, sayHello2() in questo esempio. In JavaScript, se si utilizza la parola chiave function all'interno di un'altra funzione, si sta creando una chiusura.

In C e molti altri linguaggi comuni, dopo restituisce una funzione, tutte le variabili locali non sono più accessibili perché lo stack-frame è distrutto.

In JavaScript, se dichiari una funzione all'interno di un'altra funzione, le variabili locali della funzione esterna possono rimanere accessibili dopo essere ritornate da essa. Questo è dimostrato sopra, perché chiamiamo la funzione say2() dopo che siamo tornati da sayHello2(). Nota che il codice che chiamiamo fa riferimento alla variabile text, che era una variabile locale della funzione sayHello2().

 
function() { console.log(text); } // Output of say2.toString();

Guardando l'output di say2.toString(), possiamo vedere che il codice si riferisce alla variabile text. La funzione anonima può fare riferimento a text che contiene il valore 'Hello Bob' poiché le variabili locali di sayHello2() sono state segretamente mantenute in vita in una chiusura.

Il genio è che in JavaScript un riferimento alla funzione ha anche un riferimento segreto alla chiusura in cui è stato creato - simile a come i delegati sono un puntatore del metodo più un riferimento segreto a un oggetto.

Altri esempi

Per qualche motivo, le chiusure sembrano davvero difficili da capire quando leggi su di esse, ma quando vedi alcuni esempi, diventa chiaro come funzionano (ci ho messo un po 'di tempo). Raccomando di esaminare attentamente gli esempi finché non capisci come funzionano. Se inizi a utilizzare le chiusure senza comprendere appieno il loro funzionamento, presto creerai alcuni bug davvero strani!

Esempio 3

Questo esempio mostra che le variabili locali non vengono copiate - sono mantenute per riferimento. È come se il frame dello stack rimanga vivo nella memoria anche dopo che la funzione esterna è terminata!

 
function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

Esempio 4

Tutte e tre le funzioni globali hanno un riferimento comune alla chiusura stessa perché sono tutte dichiarate all'interno di una singola chiamata a setupSomeGlobals().

 
var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

Le tre funzioni hanno accesso condiviso alla stessa chiusura: le variabili locali di setupSomeGlobals() quando sono state definite le tre funzioni.

Si noti che nell'esempio sopra, se si chiama nuovamente il setupSomeGlobals(), viene creata una nuova chiusura (stack-frame!). Le vecchie variabili gLogNumber, gIncreaseNumber, gSetNumber vengono sovrascritte con le funzioni nuove che hanno la nuova chiusura. (In JavaScript, ogni volta che dichiari una funzione all'interno di un'altra funzione, le funzioni interne vengono /vengono ricreate di nuovo ogni momento in cui viene chiamata la funzione esterna.)

Esempio 5

Questo esempio mostra che la chiusura contiene qualsiasi variabile locale che è stata dichiarata all'interno della funzione esterna prima di uscire. Si noti che la variabile alice viene effettivamente dichiarata dopo la funzione anonima. La funzione anonima viene dichiarata per prima e quando viene chiamata tale funzione può accedere alla variabile alice poiché alice si trova nello stesso ambito (JavaScript sollevamento variabile ). Anche sayAlice()() chiama direttamente il riferimento di funzione restituito da sayAlice() - è esattamente uguale a quello che è stato fatto in precedenza ma senza la variabile temporanea.

 
function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

Tricky: nota che la variabile say si trova anche all'interno della chiusura e potrebbe essere utilizzata da qualsiasi altra funzione che potrebbe essere dichiarata all'interno di sayAlice(), oppure potrebbe essere richiamata in modo ricorsivo all'interno della funzione interna.

Esempio 6

Questo è un vero trucchetto per molte persone, quindi devi capirlo. Fai molta attenzione se stai definendo una funzione all'interno di un ciclo: le variabili locali della chiusura potrebbero non agire come potresti pensare prima.

Per comprendere questo esempio, è necessario comprendere la funzione di "sollevamento variabile" in Javascript.

 
function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

La riga result.push( function() {console.log(item + ' ' + list[i])} aggiunge un riferimento a una funzione anonima tre volte alla matrice dei risultati. Se non hai familiarità con le funzioni anonime, pensa come:

 
pointer = function() {console.log(item + ' ' + list[i])};
result.push(pointer);

Notare che quando si esegue l'esempio, "item2 undefined" viene registrato tre volte! Questo perché, proprio come gli esempi precedenti, esiste una sola chiusura per le variabili locali per buildList (che sono result, i, list e item). Quando le funzioni anonime sono chiamate sulla linea fnlist[j](); tutti usano la stessa chiusura singola e usano il valore corrente per i e item all'interno di quella chiusura (dove i ha un valore di 3 perché il ciclo è stato completato e item ha un valore di 'item2'). Nota che stiamo indicizzando da 0 quindi item ha un valore di item2. E l'i ++ incrementerà i fino al valore 3.

Potrebbe essere utile vedere cosa succede quando viene utilizzata una dichiarazione a livello di blocco della variabile item (tramite la parola chiave let) invece di una dichiarazione di variabile con ambito funzionale tramite la parola chiave var. Se viene apportata tale modifica, ciascuna funzione anonima nell'array result ha la propria chiusura; quando viene eseguito l'esempio, l'output è il seguente:

 
item0 undefined
item1 undefined
item2 undefined

Se la variabile i viene anche definita utilizzando let anziché var, l'output è:

 
item0 1
item1 2
item2 3

Esempio 7

In questo ultimo esempio, ogni chiamata alla funzione principale crea una chiusura separata.

 
function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Sommario

Se tutto sembra completamente non chiaro, la cosa migliore da fare è giocare con gli esempi. Leggere una spiegazione è molto più difficile della comprensione degli esempi. Le mie spiegazioni su chiusure e riquadri, ecc. Non sono tecnicamente corrette: sono semplificazioni grossolane intese a contribuire alla comprensione. Una volta terminata l'idea di base, puoi raccogliere i dettagli più tardi.

Punti finali:

  • Ogni volta che usi function in un'altra funzione, viene utilizzata una chiusura.
  • Ogni volta che usi eval() all'interno di una funzione, viene utilizzata una chiusura. Il testo che hai eval può fare riferimento a variabili locali della funzione, e all'interno di eval puoi persino creare nuove variabili locali usando eval('var foo = …')
  • Quando usi new Function(…) (il Costruttore di funzioni ) all'interno di una funzione, non crea una chiusura. (La nuova funzione non può fare riferimento alle variabili locali della funzione esterna.)
  • Una chiusura in JavaScript è come mantenere una copia di tutte le variabili locali, proprio come erano quando una funzione è stata chiusa.
  • Probabilmente è meglio pensare che una chiusura venga sempre creata solo come una voce di una funzione, e le variabili locali vengono aggiunte a quella chiusura.
  • Un nuovo insieme di variabili locali viene mantenuto ogni volta che viene chiamata una funzione con una chiusura (dato che la funzione contiene una dichiarazione di funzione al suo interno, e un riferimento a quella funzione interna viene restituito o viene mantenuto un riferimento esterno per esso in qualche modo).
  • Due funzioni potrebbero sembrare che abbiano lo stesso testo sorgente, ma hanno un comportamento completamente diverso a causa della chiusura "nascosta". Non penso che il codice JavaScript possa effettivamente scoprire se un riferimento di funzione ha una chiusura o meno.
  • Se si sta tentando di apportare modifiche al codice sorgente dinamico (ad esempio: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), non funzionerà se myFunction è una chiusura (ovviamente non si potrebbe nemmeno pensare di eseguire la sostituzione della stringa del codice sorgente in fase di esecuzione, ma ...).
  • È possibile ottenere dichiarazioni di funzioni all'interno delle dichiarazioni di funzione all'interno delle funzioni ... e puoi ottenere chiusure a più di un livello.
  • Penso che normalmente una chiusura sia un termine sia per la funzione che per le variabili che vengono catturate. Nota che non uso quella definizione in questo articolo!
  • Ho il sospetto che le chiusure in JavaScript differiscano da quelle normalmente presenti nei linguaggi funzionali.

Link

Grazie

Se hai solo chiusure apprese (qui o altrove!), allora sono interessato a qualsiasi tuo commento su eventuali modifiche che potresti suggerire che potrebbero rendere più chiaro questo articolo. Invia una mail a morrisjohns.com (morris_closure @). Si prega di notare che io non sono un guru su JavaScript - né su chiusure.


Il post originale di Morris può essere trovato nel Internet Archive .

    
6854
2019-06-05 17: 50: 04Z
  1. Brillante. In particolare mi piace: "Una chiusura in JavaScript è come mantenere una copia di tutte le variabili locali, proprio come erano quando una funzione è stata chiusa."
    2008-09-21 14: 38: 40Z
  2. @ e-satis - Per quanto possa sembrare geniale, "una copia di tutte le variabili locali, proprio come erano quando la funzione è uscita" è fuorviante. Suggerisce che i valori delle variabili vengano copiati, ma in realtà è l'insieme di variabili che non cambia dopo che la funzione è stata chiamata (ad eccezione di 'eval' forse: blog.rakeshpai.me/2008/10/... ). Suggerisce che la funzione deve tornare prima che venga creata la chiusura, ma non è necessario che ritorni prima che la chiusura possa essere utilizzata come chiusura.
    2011-08-08 15: 24: 48Z
  3. Questo suona bene: "Una chiusura in JavaScript è come mantenere una copia di tutte le variabili locali, proprio come erano quando una funzione è stata chiusa." Ma è fuorviante per un paio di motivi. (1) La chiamata di funzione non deve uscire per creare una chiusura. (2) Non è una copia dei valori delle variabili locali ma delle variabili stesse. (3) Non dice chi ha accesso a queste variabili.
    2013-02-11 18: 20: 22Z
  4. L'Esempio 5 mostra un "gotcha" in cui il codice non funziona come previsto. Ma non mostra come risolverlo. Questa altra risposta mostra un modo per farlo.
    2013-06-24 19: 12: 21Z
  5. Mi piace come questo post inizia con grandi lettere in grassetto che dicono "Closures Are Not Magic" e termina il suo primo esempio con "La magia è che in JavaScript una funzione fa riferimento anche ha un riferimento segreto alla chiusura è stato creato in ".
    2014-09-25 02: 30: 17Z

Ogni volta che vedi la parola chiave function all'interno di un'altra funzione, la funzione inner ha accesso alle variabili nella funzione esterna.

 
function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

Questo logarà sempre 16, perché bar può accedere al x che è stato definito come argomento a foo, e può anche accedere a tmp da foo.

Che è una chiusura. Una funzione non deve return per essere chiamata una chiusura. Semplicemente l'accesso a variabili esterne all'ambito lessicale immediato crea una chiusura .

 
function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

La funzione sopra riportata registrerà anche 16, perché bar può ancora fare riferimento a x e tmp, anche se non è più direttamente all'interno dell'oscilloscopio.

Tuttavia, dal momento che tmp è ancora in agguato all'interno della chiusura del bar, anche questo viene incrementato. Verrà incrementato ogni volta che chiami bar.

L'esempio più semplice di chiusura è questo:

 
var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Quando è presente una funzione JavaScriptvocato, viene creato un nuovo contesto di esecuzione. Insieme agli argomenti della funzione e all'oggetto padre, questo contesto di esecuzione riceve anche tutte le variabili dichiarate al di fuori di esso (nell'esempio precedente, sia 'a' che 'b').

È possibile creare più di una funzione di chiusura, restituendone una lista o impostandole su variabili globali. Tutti questi si riferiscono allo stesso x e allo stesso tmp, non fanno le proprie copie.

Qui il numero x è un numero letterale. Come con altri valori letterali in JavaScript, quando viene chiamato foo, il numero x è copiato in foo come argomento x.

D'altra parte, JavaScript usa sempre riferimenti quando si tratta di oggetti. Se dici, hai chiamato foo con un oggetto, la chiusura restituita farà riferimento a quell'oggetto originale!

 
function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Come previsto, ogni chiamata a bar(10) incrementerà x.memb. Ciò che potrebbe non essere previsto, è che x si riferisce semplicemente allo stesso oggetto della variabile age! Dopo un paio di chiamate al bar, il age.memb sarà 2! Questo riferimento è la base per perdite di memoria con oggetti HTML.

    
3907
2019-05-22 12: 58: 05Z
  1. @ feeela: Sì, ogni funzione JS crea una chiusura. Le variabili che non sono referenziate saranno probabilmente rese idonee per la garbage collection nei moderni motori JS, ma non cambiano il fatto che quando si crea un contesto di esecuzione, quel contesto ha un riferimento al contesto di esecuzione che lo racchiude e alle sue variabili, e quella funzione è un oggetto che può essere ricollocato in un ambito variabile diverso, pur mantenendo quel riferimento originale. Questa è la chiusura.
    2013-08-19 01: 31: 25Z
  2. @ Ali Ho appena scoperto che il jsFiddle che ho fornito non dimostra nulla, dal momento che il delete fallisce. Ciononostante, l'ambiente lessicale che la funzione svolgerà come [[Ambito]] (e, in ultima analisi, verrà utilizzato come base per il proprio ambiente lessicale quando invocato) viene determinato quando viene eseguita l'istruzione che definisce la funzione. Ciò significa che la funzione è che chiude i contenuti INTERO dell'ambito di esecuzione, indipendentemente dai valori a cui si riferisce effettivamente e dall'eventualità che sfugga all'ambito. Per favore guarda le sezioni 13.2 e 10 in le specifiche
    2013-08-20 17: 51: 46Z
  3. Questa è stata una buona risposta finché non ha provato a spiegare i tipi primitivi e i riferimenti. Diventa completamente sbagliato e parla dei letterali copiati, che in realtà non ha nulla a che fare con niente.
    2014-07-04 14: 53: 35Z
  4. Le chiusure sono la risposta di JavaScript alla programmazione orientata agli oggetti basata su classi. JS non è basato sulla classe, quindi è stato necessario trovare un altro modo per implementare alcune cose che altrimenti non potrebbero essere implementate.
    2014-09-18 10: 45: 56Z
  5. questa dovrebbe essere la risposta accettata. La magia non avviene mai nella funzione interiore. Succede quando assegna la funzione esterna a una variabile. Questo crea un nuovo contesto di esecuzione per la funzione interna, quindi è possibile accumulare la "variabile privata". Ovviamente può, dal momento che la variabile a cui è assegnata la funzione esterna ha mantenuto il contesto. La prima risposta rende l'intera cosa più complessa senza spiegare cosa accade realmente lì.
    2016-08-18 00: 26: 28Z

PREFAZIONE: questa risposta è stata scritta quando la domanda era:

  

Come il vecchio Albert ha detto: "Se non riesci a spiegarlo a un bambino di sei anni, davvero non lo capisci da solo." Beh, ho cercato di spiegare le chiusure di JS a un amico di 27 anni e completamente fallito.

     

Qualcuno può considerare che io abbia 6 anni e sia stranamente interessato a quell'argomento?

Sono abbastanza sicuro di essere stato una delle poche persone che hanno tentato di tfai letteralmente la domanda iniziale. Da allora, la domanda è mutata più volte, quindi la mia risposta potrebbe sembrare incredibilmente stupida & fuori posto. Speriamo che l'idea generale della storia rimanga divertente per alcuni.


Sono un grande fan dell'analogia e della metafora quando spieghiamo concetti difficili, quindi lasciami provare la mia mano con una storia.

C'era una volta:

C'era una principessa ...

 
function princess() {

Ha vissuto in un mondo meraviglioso pieno di avventure. Ha incontrato il suo principe azzurro, ha girato il suo mondo su un unicorno, ha combattuto draghi, incontrato animali parlanti e molte altre cose fantastiche.

 
    var adventures = [];

    function princeCharming() { /* ... */ }

    var unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

Ma avrebbe sempre dovuto tornare al suo noioso mondo di faccende e adulti.

 
    return {

E lei spesso raccontava la sua ultima incredibile avventura come principessa.

 
        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

Ma tutto quello che vedrebbero è una bambina ...

 
var littleGirl = princess();

... raccontando storie di magia e fantasia.

 
littleGirl.story();

E anche se i grandi conoscevano le vere principesse, non avrebbero mai creduto negli unicorni o nei draghi perché non avrebbero mai potuto vederli. Gli adulti dicevano che esistevano solo nell'immaginazione della bambina.

Ma conosciamo la vera verità; quella bambina con la principessa dentro ...

... è davvero una principessa con una bambina dentro.

    
2348
2017-11-01 11: 40: 09Z
  1. Adoro questa spiegazione, davvero. Per chi lo legge e non lo segue, l'analogia è questa: la funzione princess () è un ambito complesso che contiene dati privati. Al di fuori della funzione, i dati privati ​​non possono essere visti o utilizzati. La principessa conserva gli unicorni, i draghi, le avventure ecc. Nella sua immaginazione (dati privati) e gli adulti non possono vederli da soli. MA l'immaginazione della principessa viene catturata nella chiusura per la funzione story(), che è l'unica interfaccia che l'istanza littleGirl espone nel mondo della magia.
    2013-02-28 07: 49: 20Z
  2. Quindi qui story è la chiusura ma il codice era var story = function() {}; return story;, quindi littleGirl sarebbe la chiusura. Almeno questa è l'impressione che ottengo da l'uso di MDN di "private" "metodi con chiusure : " Queste tre funzioni pubbliche sono chiusure che condividono lo stesso ambiente. "
    2016-02-23 00: 58: 37Z
  3. @ icc97, sì, story è una chiusura che fa riferimento all'ambiente fornito nell'ambito di princess. princess è anche un'altra chiusura implicita ​​i>, ovvero il princess e il littleGirl condividono qualsiasi riferimento a un array parents che sarebbe di nuovo disponibile nell'ambiente /ambito in cui esiste il littleGirl e il princess è definito.
    2016-03-01 16: 00: 04Z
  4. @ BenjaminKrupp Ho aggiunto un commento di codice esplicito per mostrare /implicare che ci sono più operazioni nel corpo di princess rispetto a ciò che è scritto. Purtroppo questa storia è un po 'fuori luogo su questo thread. Inizialmente la domanda era di "spiegare le chiusure di JavaScript a un vecchio di 5 anni"; la mia risposta è stata l'unica che ha tentato di farlo. Non dubito che sarebbe fallito miseramente, ma almeno questa risposta potrebbe aver avuto la possibilità di mantenere un interesse di 5 anni.
    2017-09-11 18: 45: 36Z
  5. In realtà, per me questo ha perfettamente senso. E devo ammettere che capire finalmente una chiusura di JS usando storie di principesse e avventure mi fa sembrare un po 'strano.
    2017-10-02 10: 03: 34Z

Affrontando seriamente la questione, dovremmo scoprire che cosa un tipico 6 anni è capace di cognitivamente, anche se, ammettiamolo, chi è interessato a JavaScript non è così tipico.

Sviluppo dell'infanzia: da 5 a 7 anni dice:

  

Il tuo bambino sarà in grado di seguire le indicazioni in due passaggi. Ad esempio, se dici a tuo figlio, "Vai in cucina e prendimiun sacchetto della spazzatura "saranno in grado di ricordare quella direzione.

Possiamo usare questo esempio per spiegare chiusure, come segue:

  

La cucina è una chiusura che ha una variabile locale, chiamata trashBags. C'è una funzione all'interno della cucina chiamata getTrashBag che ottiene un cestino e lo restituisce.

Possiamo codificare questo in JavaScript in questo modo:

 
function makeKitchen() {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A

Ulteriori punti che spiegano perché le chiusure sono interessanti:

  • Ogni volta che viene chiamato makeKitchen(), viene creata una nuova chiusura con il suo trashBags separato.
  • La variabile trashBags è locale all'interno di ogni cucina e non è accessibile all'esterno, ma la funzione interna della proprietà getTrashBag ha accesso ad essa.
  • Ogni chiamata di funzione crea una chiusura, ma non sarebbe necessario mantenere la chiusura intorno a meno che una funzione interna, che ha accesso all'interno della chiusura, possa essere chiamata dall'esterno della chiusura. Restituire l'oggetto con la funzione getTrashBag lo fa qui.
722
2018-10-10 17: 50: 14Z
  1. In realtà, in modo confuso, la funzione makeKitchen call è la chiusura effettiva, non l'oggetto cucina che restituisce.
    2016-06-27 17: 56: 00Z
  2. Passando attraverso gli altri ho trovato questa risposta come il modo più semplice per spiegare cosa e perché le closures.is.
    2016-08-12 15: 12: 01Z
  3. Troppo menu e antipasto, poca carne e patate. Potresti migliorare questa risposta con una sola frase breve come: "Una chiusura è il contesto sigillato di una funzione, per la mancanza di qualsiasi meccanismo di scoping fornito dalle classi."
    2017-05-13 16: 30: 28Z

The Straw Man

Ho bisogno di sapere quante volte è stato fatto clic su un pulsante e fare qualcosa su ogni terzo clic ...

Soluzione abbastanza ovvia

 
// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

Ora funzionerà, ma invade l'ambito esterno aggiungendo una variabile, il cui unico scopo è quello di tenere traccia del conteggio. In alcune situazioni, sarebbe preferibile in quanto l'applicazione esterna potrebbe aver bisogno di accedere a queste informazioni. Ma in questo caso, stiamo cambiando solo il comportamento di ogni terzo clic, quindi è preferibile racchiudere questa funzionalità all'interno del gestore di eventi .

Considera questa opzione

 
var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

Notate alcune cose qui.

Nell'esempio sopra, sto usando il comportamento di chiusura di JavaScript. Questo comportamento consente a qualsiasi funzione di accedere all'ambito in cui è stata creata, a tempo indeterminato. Per applicare praticamente questo, invoco immediatamente una funzione che restituisce un'altra funzione, e poiché la funzione che sto restituendo ha accesso alla variabile di conteggio interna (a causa del comportamento di chiusura spiegato sopra), questo si traduce in un ambito privato per l'utilizzo da parte della funzione risultante ... Non è così semplice? Diluiamolo ...

Una semplice chiusura a una riga

 
//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

Tutte le variabili all'esterno della funzione restituita sono disponibili per la funzione restituita, ma non sono direttamente disponibili per l'oggetto funzione restituito ...

 
func();  // Alerts "val"
func.a;  // Undefined

Compralo? Quindi nel nostro esempio principale, la variabile count è contenuta all'interno della chiusura e sempre disponibile per il gestore eventi, quindi mantiene il suo stato da click a click.

Inoltre, questo stato di variabile privata è fully accessibile, sia per le letture che per l'assegnazione alle sue variabili scope private.

Ecco qua; ora stai incapsulando completamente questo comportamento.

Post completo sul blog (incluso jQuery considerazioni)

    
560
2017-11-13 04: 54: 05Z
  1. Non sono d'accordo con la tua definizione di cosa sia una chiusura. Non c'è motivo per cui debba auto-invocare. È anche un po 'semplicistico (e inaccurato) dire che deve essere "restituito" (molte discussioni su questo nei commenti della risposta più alta a questa domanda)
    2013-02-26 19: 51: 22Z
  2. @ James anche se non sei d'accordo, il suo esempio (e l'intero post) è uno dei migliori che ho visto. Sebbene la domanda non sia vecchia e risolta per me, merita assolutamente un +1.
    2013-02-27 11: 20: 09Z
  3. "Ho bisogno di sapere quante volte è stato cliccato un pulsante, e fare qualcosa su ogni terzo clic ..." QUESTO ha ottenuto il mio Attenzione. Un caso d'uso e la soluzione che mostra come una chiusura non è una cosa così misteriosa e che molti di noi hanno scritto ma non conoscevano esattamente il nome ufficiale.
    2014-01-10 13: 49: 37Z
  4. Bel esempio perché mostra che "count" nel secondo esempio mantiene il valore di "count" e non reimposta a 0 ogni volta che si fa clic su "element". Molto informativo!
    2014-07-21 06: 19: 54Z
  5. + 1 per il comportamento di chiusura . Possiamo limitare il comportamento di chiusura alle funzioni in javascript o questo concetto può essere applicato anche ad altre strutture del linguaggio?
    2015-03-08 19: 32: 34Z

Le chiusure sono difficili da spiegare perché sono usate per fare un lavoro comportamentale che tutti si aspettano intuitivamente di lavorare comunque. Trovo che il modo migliore per spiegarle (e il modo in cui I ho imparato quello che fanno) è immaginare la situazione senza di loro:

 
    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

Cosa succederebbe qui se JavaScript non conoscesse chiusure? Basta sostituire la chiamata nell'ultima riga con il suo corpo del metodo (che è fondamentalmente ciò che le chiamate di funzione fanno) e ottieni:

 
console.log(x + 3);

Ora, dov'è la definizione di x? Non l'abbiamo definito nell'ambito corrente. L'unica soluzione è lasciare che plus5 porti il suo ambito (o meglio, l'ambito del genitore). In questo modo, x è ben definito ed è associato al valore 5.

    
472
2017-03-17 08: 51: 15Z
  1. Sono d'accordo. Dare un aiuto ai nomi significativi delle funzioni invece dei tradizionali "folletti" mi aiuta molto. Conta la semantica.
    2010-04-08 14: 16: 40Z
  2. così in una pseudo-lingua, è fondamentalmente come alert(x+3, where x = 5). Il where x = 5 è la chiusura. Ho ragione?
    2010-12-22 09: 52: 33Z
  3. @ Jus12: esattamente. Dietro le quinte, una chiusura è solo uno spazio in cui sono memorizzati i valori delle variabili correnti ("binding"), come nell'esempio.
    2010-12-22 11: 28: 09Z
  4. Questo è esattamente il tipo di esempio che induce molte persone a pensare che siano i valori che vengono utilizzati nella funzione restituita, non il variabile variabile stessa. Se fosse cambiato in "return x + = y", o meglio ancora in quello e in un'altra funzione "x * = y", allora sarebbe chiaro che non è stato copiato nulla. Per le persone utilizzate per lo stack di frame, immagina di utilizzare invece i frame heap, che possono continuare a esistere anche dopo il ritorno della funzione.
    2013-06-21 12: 36: 49Z
  5. @ Matt Non sono d'accordo. Un esempio è not che dovrebbe documentare esaustivamente tutte le proprietà. È pensato per essere riduttivo e illustrare la caratteristica saliente di un concetto. L'OP ha chiesto una spiegazione semplice ("per un bambino di sei anni"). Prendi la risposta accettata: completamente fallisce afornire una spiegazione concisa, proprio perché cerca di essere esaustivo. (Sono d'accordo con te sul fatto che è un'importante proprietà di JavaScript che l'associazione è per riferimento piuttosto che per valore ... ma, ancora una volta, una spiegazione riuscita è quella che si riduce al minimo.)
    2013-06-21 14: 30: 42Z

Questo è un tentativo di chiarire alcuni (possibili) equivoci sulle chiusure che appaiono in alcune delle altre risposte.

  • Una chiusura non viene creata solo quando si restituisce una funzione interna. Di fatto, la funzione di inclusione non ha bisogno di tornare affatto per poter creare la chiusura. Potresti invece assegnare la tua funzione interiore a una variabile in un ambito esterno o passarla come argomento a un'altra funzione in cui potrebbe essere richiamata immediatamente o in qualsiasi momento successivo. Pertanto, la chiusura della funzione di chiusura è probabilmente creata non appena viene chiamata la funzione di chiusura poiché qualsiasi funzione interna ha accesso a tale chiusura ogni volta che viene richiamata la funzione interna, prima o dopo il ritorno della funzione di chiusura.
  • Una chiusura non fa riferimento a una copia dei vecchi valori delle variabili nel suo ambito. Le variabili stesse fanno parte della chiusura e quindi il valore visualizzato quando si accede a una di quelle le variabili è l'ultimo valore al momento dell'accesso. Questo è il motivo per cui le funzioni interne create all'interno di loop possono essere complicate, dato che ognuna ha accesso alle stesse variabili esterne invece di afferrare una copia delle variabili nel momento in cui la funzione viene creata o chiamata.
  • Le "variabili" in una chiusura includono qualsiasi funzione denominata ​​strong> dichiarata all'interno della funzione. Includono anche argomenti della funzione. Una chiusura ha anche accesso alle sue variabili di chiusura contenenti, fino all'ambito globale.
  • Le chiusure utilizzano la memoria, ma non causano perdite di memoria poiché JavaScript di per sé ripulisce le proprie strutture circolari a cui non fa riferimento. Le perdite di memoria di Internet Explorer che coinvolgono le chiusure vengono create quando non riesce a disconnettere i valori degli attributi del DOM che fanno riferimento alle chiusure, mantenendo quindi i riferimenti a possibili strutture circolari.
359
2016-05-05 15: 00: 49Z
  1. A proposito, ho aggiunto questa "risposta" con dei chiarimenti per non indirizzare direttamente la domanda originale. Invece, spero che qualsiasi risposta semplice (per un bambino di 6 anni) non introduca nozioni errate su questo argomento complesso. Per esempio. la famosa risposta wiki sopra dice "Una chiusura è quando si restituisce la funzione interiore". Oltre a essere grammaticalmente sbagliato, questo è tecnicamente sbagliato.
    2011-07-21 14: 15: 29Z
  2. James, ho detto che la chiusura è "probabilmente" creata al momento della chiamata della funzione di chiusura perché è plausibile che un'implementazione potrebbe rinviare la creazione di una chiusura fino a qualche tempo dopo, quando decide che una chiusura è assolutamente necessaria. Se non è definita alcuna funzione interna nella funzione di chiusura, non sarà necessaria alcuna chiusura. Quindi forse potrebbe aspettare che venga creata la prima funzione interna per creare una chiusura dal contesto di chiamata della funzione di inclusione.
    2012-07-10 17: 27: 03Z
  3. @ Beetroot-Beetroot Supponiamo di avere una funzione interna che viene passata ad un'altra funzione dove è usata prima la funzione esterna ritorna, e supponiamo restituiamo anche la stessa funzione interna dalla funzione esterna. È identicamente la stessa funzione in entrambi i casi, ma stai dicendo che prima che la funzione esterna ritorni, la funzione interna è "vincolata" allo stack di chiamate, mentre dopo che ritorna, la funzione interna viene improvvisamente vincolata a una chiusura. Si comporta in modo identico in entrambi i casi; la semantica è identica, quindi non stai parlando solo dei dettagli di implementazione?
    2012-10-16 16: 06: 13Z
  4. @ Beetroot-Beetroot, grazie per il tuo feedback, e sono felice di averti pensato. Non vedo ancora alcuna differenza semantica tra il contesto live della funzione esterna e quello stesso contesto quando diventa una chiusura quando la funzione ritorna (se iocapisci la tua definizione). La funzione interiore non interessa. La garbage collection non si cura perché la funzione inner mantiene un riferimento al contesto /chiusura in entrambi i casi, e il chiamante della funzione esterna non fa altro che cedere il suo riferimento al contesto della chiamata. Ma è fonte di confusione per le persone e forse è meglio chiamarlo contesto di una chiamata.
    2012-10-18 00: 26: 54Z
  5. Questo articolo è difficile da leggere, ma penso che in realtà supporti ciò che sto dicendo. Dice: "Una chiusura viene formata restituendo un oggetto funzione [...] o assegnando direttamente un riferimento a tale oggetto funzione ad, ad esempio, una variabile globale." Non voglio dire che GC è irrilevante. Piuttosto, a causa di GC, e poiché la funzione interna è collegata al contesto di chiamata della funzione esterna (o [[scope]] come dice l'articolo), allora non importa se la chiamata di funzione esterna ritorna perché quella associazione con l'interno la funzione è la cosa importante.
    2012-10-21 01: 49: 59Z

OK, fan delle chiusure di 6 anni. Vuoi ascoltare l'esempio più semplice di chiusura?

Immaginiamo la prossima situazione: un autista è seduto in una macchina. Quella macchina è dentro un aereo. L'aereo è in aeroporto. La capacità del guidatore di accedere a cose al di fuori della sua auto, ma all'interno dell'aereo, anche se quell'aereo lascia un aeroporto, è una chiusura. Questo è tutto. Alla svolta del 27, consulta la spiegazione più dettagliata o nell'esempio riportato di seguito.

Ecco come posso convertire la mia storia piana nel codice.

 
var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");
    
357
2018-10-10 18: 38: 01Z
  1. Ben giocato e risponde al poster originale. Penso che questa sia la migliore risposta. Stavo per usare i bagagli in un modo simile: immagina di andare a casa della nonna e ti metti la valigetta Nintendo DS con le carte da gioco all'interno della valigia, ma poi metti la custodia nello zaino e metti le carte da gioco nelle tasche dello zaino, e ALLORA hai messo il tutto in una grande valigia con più carte da gioco nelle tasche della valigia. Quando arrivi a casa della nonna, puoi giocare qualsiasi gioco sul tuo DS fintanto che tutte le casse esterne sono aperte. o qualcosa del genere.
    2013-09-19 00: 37: 32Z

Una chiusura è molto simile a un oggetto. Viene istanziato ogni volta che si chiama una funzione.

L'ambito di una chiusura in JavaScript è lessicale, il che significa che tutto ciò che è contenuto nella funzione alla chiusura appartiene a, ha accesso a qualsiasi variabile che è in esso.

Una variabile è contenuta nella chiusura se tu

  1. assegnalo con var foo=1; o
  2. scrivi solo var foo;

Se una funzione interna (una funzione contenuta all'interno di un'altra funzione) accede a tale variabile senza definirla nel proprio ambito con var, modifica il contenuto della variabile nella chiusura esterna p>

Una chiusura sopravvive al runtime della funzione che lo ha generato. Se altre funzioni escono dalla chiusura /ambito in cui sono definite (ad esempio come valori di ritorno), quelle continueranno a fare riferimento a chiusura .

Esempio

 
function example(closure) {
  // define somevariable to live in the closure of example
  var somevariable = 'unchanged';

  return {
    change_to: function(value) {
      somevariable = value;
    },
    log: function(value) {
      console.log('somevariable of closure %s is: %s',
        closure, somevariable);
    }
  }
}

closure_one = example('one');
closure_two = example('two');

closure_one.log();
closure_two.log();
closure_one.change_to('some new value');
closure_one.log();
closure_two.log();

uscita ​​h2>  
somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
    

352
2018-10-10 18: 39: 20Z
  1. Wow, non hai mai saputo che potresti usare le sostituzioni di stringa in console.log in questo modo. Se qualcun altro è interessato, ce ne sono altri: developer.mozilla.org/en -USA /docs /DOM /...
    2013-01-04 02: 59: 32Z
  2. Anche le variabili presenti nell'elenco dei parametri della funzione fanno parte della chiusura (ad esempio non solo limitato a var).
    2015-03-18 03: 38: 09Z
  3. Le chiusure suonano più come oggetti e classi ecc. Non sono sicuro perché molte persone non li confrontano - sarebbe più facile per noi novizi imparare!
    2019-05-23 12: 37: 10Z

Ho scritto un post sul blog un po 'indietro spiegando chiusure. Ecco cosa ho detto sulle chiusure in termini di perché ne vorresti uno

  

Le chiusure sono un modo per lasciare una funzione    variabili private persistenti :   cioè, variabili che solo una   la funzione sa, dove può   tenere traccia delle informazioni dei tempi precedenti   che è stato eseguito.

In questo senso, consentono a una funzione di comportarsi un po 'come un oggetto con attributi privati.

Post completo:

Allora, quali sono queste cose di chiusura?

    
230
2013-01-28 02: 23: 21Z
  1. Quindi il vantaggio principale delle chiusure potrebbe essere enfatizzato con questo esempio? Dire che ho una funzione emailError (sendToAddress, errorString) Potrei quindi dire devError = emailError("devinrhode2@googmail.com", errorString) e poi avere la mia versione personalizzata di una funzione emailError condivisa?
    2011-07-31 06: 42: 49Z
  2. Dopo aver spalato la strada attraverso un sacco di "splaining", ho iniziato a capire finalmente a cosa servissero. Ho pensato a me stesso "oh, è come variabili private in un oggetto?" e bam. Questa è stata la prossima risposta che ho letto.
    2012-12-30 21: 20: 07Z
  3. Questa spiegazione e l'esempio perfetto associato nel link a (thingys di chiusura) è il modo migliore per comprendere le chiusure e dovrebbe essere proprio al top!
    2019-05-05 09: 45: 39Z

Le chiusure sono semplici:

Il seguente esempio semplice copre tutti i punti principali delle chiusure di JavaScript. *

Ecco una fabbrica che produce calcolatrici che possono aggiungere e moltiplicare:

 
function make_calculator() {
  var n = 0; // this calculator stores a single number n
  return {
    add: function(a) {
      n += a;
      return n;
    },
    multiply: function(a) {
      n *= a;
      return n;
    }
  };
}

first_calculator = make_calculator();
second_calculator = make_calculator();

first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400

first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

Il punto chiave: ogni chiamata a make_calculator crea una nuova variabile locale n, che continua a essere utilizzabile dalle funzioni add e multiply della calcolatrice molto tempo dopo il ritorno di make_calculator.

Se hai familiarità con i frame dello stack, questi calcolatori sembrano strani: come possono continuare ad accedere a n dopo i resi di make_calculator? La risposta è immaginare che JavaScript non usi "stack frames", ma usi invece "heap frames", che può persistere dopo la chiamata alla funzione che li ha resi.

Le funzioni interne come add e multiply, che accedono alle variabili dichiarate in una funzione esterna ** , sono chiamate chiusure .

Questo è praticamente tutto quello che c'è da chiudere.


* Ad esempio, copre tutti i punti nell'articolo "Closures for Dummies" fornito in un'altra risposta , tranne l'esempio 6, che mostra semplicemente che le variabili possono essere utilizzate prima di essere dichiarate, un fatto piacevole da sapere ma completamente estraneo alle chiusure. Copre inoltre tutti i punti in la risposta accettata , ad eccezione dei punti (1) che le funzioni copiano i loro argomenti in variabili locali (gli argomenti della funzione con nome) e (2) che la copia dei numeri crea un nuovo numero, ma la copia di un riferimento a un oggetto ti fornisce un altro riferimento allo stesso oggetto. Questi sono anche buoni da sapere ma ancora completamente estranei alle chiusure. È anche molto simile all'esempio in questa risposta ma un po 'più breve e meno astratto. Non copre il punto questa risposta o questo commento , ovvero che JavaScript rende difficile la connessione il valore corrente di una variabile loop nella tua funzione interna: il passaggio "plug-in" può essere fatto solo con una funzione helper che racchiude la tua funzione interna e viene invocata su ogni iterazione del ciclo. (In senso stretto, la funzione interna accede alla copia della variabile della funzione di aiuto, piuttosto che avere qualcosa collegato.) Ancora una volta, molto utile quando si creano chiusure, ma non fa parte di cosa sia una chiusura o come funzioni. C'è ulteriore confusione dovuta alle chiusure che funzionano in modo diverso nei linguaggi funzionali come ML, dove le variabili sono legate ai valori piuttosto che allo spazio di archiviazione, fornendo un flusso costante di persone che comprendono le chiusure in un modo (vale a dire il "modo di collegamento") che è semplicemente errato per JavaScript, dove le variabili sono sempre legate allo spazio di archiviazione e mai ai valori.

** Qualsiasi funzione esterna, se diversi sono nidificati, o anche nel contesto globale, come questa risposta indica chiaramente.

    
210
2017-05-23 12: 10: 46Z
  1. Che cosa succederebbe se avessi chiamato: second_calculator = first_calculator (); invece di second_calculator = make_calculator (); ? Dovrebbe essere lo stesso, vero?
    2016-10-07 05: 02: 17Z
  2. @ Ronen: Poiché first_calculator è un oggetto (non una funzione) non si dovrebbe usare parentesi in second_calculator = first_calculator;, poiché si tratta di un compito, non di una chiamata di funzione. Per rispondere alla tua domanda, ci sarebbe solo una chiamata a make_calculator, quindi verrebbe creata una sola calcolatrice e le variabili first_calculator e second_calculator si riferirebbero entrambe alla stessa calcolatrice, quindi le risposte sarebbero 3, 403, 4433, 44330.
    2016-10-10 09: 49: 48Z

Come lo spiegherei a un bambino di sei anni:

Sai come gli adulti possono possedere una casa e la chiamano casa? Quando una mamma ha un figlio, il bambino non possiede proprio nulla, giusto? Ma i suoi genitori possiedono una casa, quindi ogni volta che qualcuno chiede al bambino "Dov'è la tua casa?", Lui /lei può rispondere "a quella casa!", E indica la casa dei suoi genitori. Una "chiusura" è la capacità del bambino di essere sempre in grado (anche all'estero) di dire che ha una casa, anche se è proprio il genitore a possedere la casa.

    
203
2016-01-16 02: 30: 44Z

Puoi spiegare chiusure a a 5 anni? *

Penso ancora che la spiegazione di Google funzioni molto bene e è conciso:

 
/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

Prova che questo esempio crea una chiusura anche se la funzione interna non restituisce

* Una domanda C #

    
195
2017-05-23 11: 47: 32Z
  1. Se leggi la descrizione, vedrai che il tuo esempio non è corretto. La chiamata a innerFunction rientra nell'ambito della funzione esterna e non, come dice la descrizione, dopo il ritorno della funzione esterna. Ogni volta che si chiama outerFunction, viene creata una nuova innerfunction che viene utilizzata nell'ambito.
    2010-12-06 16: 11: 06Z
  2. @ Moss che non è un mio commento, sono uno sviluppatore di Google
    2010-12-06 23: 09: 14Z
  3. Vedendo che innerFunction non fa riferimento all'esterno dell'ambito di ExternalFunction, l'interprete è abbastanza intelligenteper vedere che non ha bisogno di chiusura?
    2011-03-07 05: 49: 31Z
  4. Il codice è "corretto", come un esempio di chiusura, anche se non affronta la parte del commento sull'utilizzo della chiusura dopo il ritorno di ExternalFunction. Quindi non è un grande esempio. Esistono molti altri modi per utilizzare una chiusura che non implichi il ritorno della funzione interna. per esempio. innerFunction potrebbe essere passato a un'altra funzione dove viene chiamato immediatamente o memorizzato e chiamato qualche tempo dopo, e in tutti i casi, ha accesso al contesto ExternalFunction che è stato creato quando è stato chiamato.
    2011-08-04 14: 01: 11Z
  5. @ syockit No, Moss ha torto. Viene creata una chiusura indipendentemente dal fatto che la funzione sfugga mai allo scope in cui è definita, e un riferimento creato in modo incondizionato all'ambiente lessicale del genitore rende tutte le variabili nello scope padre disponibili per tutte le funzioni, indipendentemente da se sono invocati all'esterno o all'interno dell'ambito in cui sono stati creati.
    2013-08-21 13: 41: 18Z

Tendo a imparare meglio con i confronti GOOD /BAD. Mi piace vedere il codice di lavoro seguito da un codice non funzionante che qualcuno potrebbe incontrare. Metto insieme un jsFiddle che fa un confronto e cerca di ridurre le differenze alle spiegazioni più semplici che potrei trovare con.

Chiusure fatte a destra:

 
console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • Nel codice precedente createClosure(n) è invocato in ogni iterazione del ciclo. Nota che ho chiamato la variabile n per evidenziare che è una nuova variabile creata in un nuovo ambito di funzione e non è la stessa variabile di index che è associata allo scope esterno.

  • Questo crea un nuovo ambito e n è associato a tale ambito; questo significa che abbiamo 10 ambiti separati, uno per ogni iterazione.

  • createClosure(n) restituisce una funzione che restituisce n all'interno di tale ambito.

  • All'interno di ogni ambito n è associato a qualsiasi valore che aveva quando veniva invocato createClosure(n), quindi la funzione nidificata che viene restituita restituirà sempre il valore di n che aveva quando fu invocato createClosure(n).

Chiusure fatte male:

 
console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • Nel codice precedente il ciclo è stato spostato all'interno della funzione createClosureArray() e la funzione ora restituisce solo l'array completato, che a prima vista sembra più intuitivo.

  • Ciò che potrebbe non essere ovvio è che poiché createClosureArray() viene invocato solo una volta viene creato un solo scope per questa funzione invece di uno per ogni iterazione del ciclo.

  • All'interno di questa funzione viene definita una variabile denominata index. Il ciclo viene eseguito e aggiunge funzioni alla matrice che restituiscono index. Nota che index è definito all'interno della funzione createClosureArray che viene sempre richiamata una sola volta.

  • Poiché all'interno della funzione createClosureArray() era presente un solo ambito, index è associato solo a un valore all'interno di tale ambito. In altre parole, ogni volta che il ciclo cambia il valore di index, lo modifica per tutto ciò che fa riferimento a tale ambito.

  • Tutte le funzioni aggiunte all'array restituiscono la variabile SAME index dall'ambito padre in cui è stata definita invece di 10 diverse da 10 diversi ambiti come il primo esempio. Il risultato finale è che tutte e 10 le funzioni restituiscono la stessa variabile dallo stesso ambito.

  • Dopo che il ciclo è terminato e index è stato modificato, il valore finale era 10, quindi ogni funzione aggiunta all'array restituisce il valore della singola variabile index che ora è impostata su 10.

Risultato

  

CHIUSURE FATTE A DESTRA
  n = 0
  n = 1
  n = 2
  n = 3
  n = 4
  n = 5
  n = 6
  n = 7
  n = 8
  n = 9

     

CHIUSURA FATTA ERRATA
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10
  n = 10

    
169
2017-03-27 17: 56: 11Z
  1. Bella aggiunta, grazie. Solo per renderlo più chiaro si può immaginare come viene creata la matrice "cattiva" nel ciclo "cattivo" con ogni iterazione: 1a iterazione: [function () {return 'n =' + 0;}] 2nd iteration:[(function () {return 'n =' + 1;}), (function () {return 'n =' + 1;})] 3rd iteration: [(function () {return 'n =' + 2; }), (function () {return 'n =' + 2;}), (function () {return 'n =' + 2;})] ecc. Quindi, ogni volta che il valore dell'indice cambia, si riflette in tutte le funzioni già aggiunte all'array.
    2016-04-08 22: 38: 08Z
  2. L'uso di let per var corregge la differenza.
    2017-10-16 10: 25: 19Z
  3. Non è qui "Chiusura conclusa a destra" è un esempio di "chiusura dentro chiusura"?
    2017-12-29 10: 17: 01Z
  4. Voglio dire, ogni funzione è tecnicamente una chiusura, ma la parte importante è che la funzione definisce una nuova variabile all'interno. La funzione che ottiene restituisce solo i riferimenti n creati in una nuova chiusura. Abbiamo appena restituito una funzione in modo che possiamo memorizzarla nell'array e richiamarla in un secondo momento.
    2017-12-29 18: 52: 58Z
  5. Se si desidera archiviare il risultato nell'array nella prima iterazione, è possibile inserirlo in questo modo: arr[index] = (function (n) { return 'n = ' + n; })(index);. Ma poi stai memorizzando la stringa risultante nell'array piuttosto che una funzione per invocare quale sconfigge il punto del mio esempio.
    2017-12-29 18: 54: 55Z

Wikipedia sulle chiusure :

  

In informatica, una chiusura è una funzione insieme a un ambiente di riferimento per i nomi non locali (variabili libere) di tale funzione.

Tecnicamente, in JavaScript , ogni funzione è una chiusura . Ha sempre accesso a variabili definite nell'ambito circostante.

Dato che la definizione di scope in JavaScript è una funzione , non un blocco di codice come in molti altri linguaggi, cosa intendiamo per chiusura in JavaScript è una funzione che funziona con variabili non locali definite nella funzione circostante già eseguita ​​strong>.

Le chiusure sono spesso utilizzate per creare funzioni con alcuni dati privati ​​nascosti (ma non è sempre il caso).

 
var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

EMS

L'esempio precedente utilizza una funzione anonima, che è stata eseguita una volta. Ma non deve essere. Può essere denominato (ad esempio mkdb) ed eseguito in seguito, generando una funzione di database ogni volta che viene richiamato. Ogni funzione generata avrà il proprio oggetto di database nascosto. Un altro esempio di utilizzo delle chiusure è quando non restituiamo una funzione, ma un oggetto che contiene più funzioni per scopi diversi, ognuna delle quali ha accesso agli stessi dati.

    
161
2013-12-18 16: 48: 34Z
  1. Questa è la migliore spiegazione per le chiusure di JavaScript. Dovrebbe essere la risposta scelta. Il resto è abbastanza divertente ma questo è effettivamente utile in modo pratico per i codificatori JavaScript del mondo reale.
    2018-02-04 12: 32: 46Z

Ho creato un tutorial interattivo su JavaScript per spiegare come funzionano le chiusure. Che cos'è una chiusura?

Ecco uno degli esempi:

 
var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here
    
135
2014-10-25 22: 38: 03Z
  

I bambini ricorderanno sempre i segreti che hanno condiviso con i loro genitori, anche dopo che i loro genitori lo sono   andato. Questo è ciò che le chiusure sono per le funzioni.

I segreti per le funzioni JavaScript sono la variabile privatas

 
var parent = function() {
 var name = "Mary"; // secret
}

Ogni volta che lo chiami, viene creata la variabile locale "nome" e viene dato il nome "Mary". E ogni volta che la funzione esce, la variabile viene persa e il nome viene dimenticato.

Come puoi immaginare, poiché le variabili vengono ricreate ogni volta che viene chiamata la funzione, e nessun altro le conoscerà, ci deve essere un luogo segreto in cui sono memorizzate. Potrebbe essere chiamato Camera dei segreti o stack o ambito locale ma non ha molta importanza. Sappiamo che sono lì, da qualche parte, nascosti nella memoria.

Ma in JavaScript c'è questa cosa molto speciale che le funzioni che sono create all'interno di altre funzioni, possono anche conoscere le variabili locali dei loro genitori e tenerle per tutto il tempo in cui vivono.

 
var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

Quindi, finché siamo nella funzione genitore, può creare una o più funzioni figlio che condividono le variabili segrete dal luogo segreto.

Ma la cosa triste è che se il bambino è anche una variabile privata della sua funzione genitore, morirebbe anche quando il genitore terminerà, e i segreti morirebbero con loro.

Quindi, per vivere, il bambino deve partire prima che sia troppo tardi

 
var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 

E ora, anche se Mary è "non più in esecuzione", il ricordo di lei non è perso e il suo bambino ricorderà sempre il suo nome e altri segreti che hanno condiviso durante il loro tempo insieme.

Quindi, se chiami il bambino "Alice", risponderà

 
child("Alice") => "My name is Alice, child of Mary"

Questo è tutto quello che c'è da dire.

    
124
2017-07-13 11: 27: 32Z
  1. Questa è la spiegazione che ha più senso per me perché non presuppone una conoscenza preliminare significativa dei termini tecnici. La spiegazione più votata qui assume che la persona che non comprende le chiusure abbia una piena e completa comprensione di termini come "scope lessicale" e "contesto di esecuzione" - mentre posso capire questi concetti concettualmente, non penso di essere come comodo con i dettagli di loro come dovrei essere, e la spiegazione senza alcun gergo è affatto ciò che ha reso le chiusure finalmente clicca per me, grazie. Come bonus, penso che spieghi anche quale ambito sia molto conciso.
    2015-05-17 20: 30: 27Z

Non capisco perché le risposte siano così complesse qui.

Ecco una chiusura:

 
var a = 42;

function b() { return a; }

Sì. Probabilmente lo usi più volte al giorno.

  

Non c'è motivo di credere che le chiusure siano un trucco complesso per risolvere problemi specifici. No, le chiusure servono solo a usare una variabile che proviene da un ambito superiore dalla prospettiva di dove è stata dichiarata la funzione (non eseguita) .

     

Ora ciò che consente di fare può essere più spettacolare, vedere altre risposte.

    
102
2015-02-21 23: 48: 56Z
  1. Questa risposta non sembra in grado di aiutare le persone non confuse. Un equivalente approssimativo in un linguaggio di programmazione tradizionale potrebbe essere quello di creare b () come metodo su un oggetto che anche ha una costante o proprietà privata a. A mio parere, la sorpresa è che l'oggetto dello scope JS fornisce effettivamente a come proprietà piuttosto che come costante. E noterai questo comportamento importante solo se lo modifichi, come in return a++;
    2015-05-15 01: 34: 07Z
  2. Esattamente quello che ha detto Jon. Prima di chiudere definitivamente le chiusure ho avuto difficoltà a trovare esempi pratici. Sì, floribon ha creato una chiusura, ma a me non istruiti questo non mi avrebbe insegnato assolutamente nulla.
    2015-10-29 23: 15: 01Z
  3. Questo non definisce cosa sia una chiusura - è semplicemente un esempio che ne usa uno. E non affronta la sfumatura di ciò che accade quando termina l'ambito; Non penso che qualcuno abbia una domanda sull'ambito del lessic scope quando tutti gli ambiti sono ancora in circolazione, e specialmente nel caso di una variabile globale.
    2016-08-09 15: 51: 36Z

Esempio per il primo punto di dlaliberte:

  

Una chiusura non viene creata solo quando si restituisce una funzione interna. In effetti, la funzione di chiusura non ha bisogno di tornare affatto. Si potrebbe invece assegnare la propria funzione interiore a una variabile in un ambito esterno o passarla come argomento a un'altra funzione in cui potrebbe essere utilizzata immediatamente. Pertanto, la chiusura della funzione di chiusura probabilmente esiste già nel momento in cui è stata chiamata la funzione di inclusione poiché ogni funzione interna ha accesso ad essa non appena viene chiamata.

 
var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
    
91
2016-01-16 02: 39: 35Z
  1. FYI: eseguendo quanto sopra mostra = > 9
    2010-05-19 20: 24: 03Z
  2. Piccolo chiarimento su una possibile ambiguità. Quando ho detto "In effetti, la funzione di chiusura non ha bisogno di tornare affatto". Non intendevo "non restituire alcun valore" ma "ancora attivo". Quindi l'esempio non mostra quell'aspetto, sebbene mostri un altro modo in cui la funzione interna può essere passata allo scope esterno. Il punto principale che stavo cercando di fare riguarda il tempo della creazione della chiusura (per la funzione di chiusura), dal momento che alcune persone sembrano pensare che ciò accada quando la funzione di inclusione ritorna. È necessario un esempio diverso per mostrare che la chiusura viene creata quando una funzione è chiamata ​​i>.
    2011-07-21 14: 03: 12Z

Una chiusura è dove una funzione interna ha accesso alle variabili nella sua funzione esterna. Questa è probabilmente la spiegazione più semplice che puoi ottenere per le chiusure.

    
86
2012-12-24 11: 10: 56Z
  1. Questa è solo metà della spiegazione. La cosa importante da notare sulle chiusure è che se la funzione interna è ancora riferita dopo che la funzione esterna è stata abbandonata, i vecchi valori della funzione esterna sono ancora disponibili per quello interno.
    2008-09-21 22: 29: 13Z
  2. In realtà, non sono i vecchi valori della funzione esterna che sono disponibili per la funzione interna, ma le vecchie variabili , che potrebbe avere nuovi valori se alcune funzioni fossero in grado di cambiarli.
    2012-08-16 02: 39: 42Z

So che ci sono già molte soluzioni, ma suppongo che questo script piccolo e semplice possa essere utile per dimostrare il concetto:

 
// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined
    
84
2016-05-09 11: 32: 47Z

Stai dormendo e inviti Dan. Dì a Dan di portare un controller XBox.

Dan invita Paul. Dan chiede a Paul di portare un controller. Quanti controllori sono stati portati alla festa?

 
function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");
    
81
2011-07-20 15: 16: 26Z

Le funzioni JavaScript possono accedere a:

  1. Argomenti
  2. Locali (ovvero le loro variabili locali e le funzioni locali)
  3. Ambiente, che include:
    • globals, incluso il DOM
    • qualsiasi cosa nelle funzioni esterne

Se una funzione accede al suo ambiente, la funzione è una chiusura.

Si noti che le funzioni esterne non sono necessarie, sebbene offrano vantaggi di cui non discuto qui. Accedendo ai dati nel proprio ambiente, una chiusura mantiene vivi i dati. Nella subcaSe delle funzioni esterne /interne, una funzione esterna può creare dati locali ed eventualmente uscire, e tuttavia, se una o più funzioni interne sopravvivono dopo che la funzione esterna è terminata, la funzione interna (s) mantiene in vita i dati locali della funzione esterna.

Esempio di chiusura che utilizza l'ambiente globale:

Immagina che gli eventi Button Overflow Vote-Up e Vote-Down siano implementati come chiusure, voteUp_click e voteDown_click, che hanno accesso a variabili esterne isVotedUp e isVotedDown, che sono definite globalmente. (Per ragioni di semplicità, mi riferisco ai pulsanti di votazione delle domande di StackOverflow, non alla serie di pulsanti Rispondi voto.)

Quando l'utente fa clic sul pulsante VoteUp, la funzione voteUp_click verifica se isVotedDown == true per determinare se votare o semplicemente annullare un voto basso. La funzione voteUp_click è una chiusura perché sta accedendo al suo ambiente.

 
var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}

Tutte e quattro queste funzioni sono chiusure in quanto tutte accedono al loro ambiente.

    
77
2016-06-08 22: 16: 22Z

L'autore di Closures ha spiegato le chiusure abbastanza bene, spiegando il motivo perché abbiamo bisogno di loro e anche di spiegare LexicalEnvironment, che è necessario per capire chiusure.
Ecco il riassunto:

Che cosa succede se si accede a una variabile, ma non è locale? Come qui:

 Inserisci la descrizione dell'immagine qui

In questo caso, l'interprete trova la variabile nel file esterno LexicalEnvironment oggetto.

Il processo consiste in due passaggi:

  1. Innanzitutto, quando viene creata una funzione f, non viene creata in un campo vuoto spazio. C'è un oggetto LexicalEnvironment corrente. Nel caso sopra, è la finestra (a non è definita al momento della funzione creazione).

 Inserisci la descrizione dell'immagine qui

Quando viene creata una funzione, ottiene una proprietà nascosta, denominata [[Ambito]], che fa riferimento all'attuale ambiente Lexical.

 Inserisci la descrizione dell'immagine qui

Se una variabile viene letta, ma non può essere trovata da nessuna parte, viene generato un errore.

Funzioni annidate

Le funzioni possono essere annidate l'una dentro l'altra, formando una catena di ambienti Lexical che può anche essere definita una catena di portata.

 Inserisci la descrizione dell'immagine qui

Quindi, la funzione g ha accesso a g, ae f.

Chiusure

Una funzione nidificata può continuare a vivere dopo che la funzione esterna ha terminato:

 Inserisci la descrizione dell'immagine qui

Marcatura di ambienti lessicali:

 Inserisci la descrizione dell'immagine qui

Come si vede, this.say è una proprietà nell'oggetto utente, quindi continua a vivere dopo aver completato l'utente.

E se ricordi, quando viene creato this.say, esso (come ogni funzione) ottiene un riferimento interno this.say.[[Scope]] all'attuale ambiente Lexical. Quindi, il LexicalEnvironment dell'esecuzione corrente dell'utente rimane in memoria. Tutte le variabili dell'utente sono anche le sue proprietà, quindi sono anche tenute con cura, non annullate come al solito.

Il punto è assicurarsi che se la funzione interna vuole accedere a una variabile esterna in futuro, è in grado di farlo.

Per riassumere:

  1. La funzione interiore mantiene un riferimento all'esterno LexicalEnvironment.
  2. La funzione interna può accedere a variabili da essa in qualsiasi momento anche se la funzione esterna è terminata.
  3. Il browser mantiene in memoria il LexicalEnvironment e tutte le sue proprietà (variabili) finché non vi è una funzione interna che la fa riferimento.

Si chiama chiusura.

    
75
2018-05-14 20: 51: 20Z

Come padre di un bambino di 6 anni, attualmente insegnando ai bambini piccoli (e un parente inesperto alla codifica senza istruzione formale, saranno necessarie correzioni), penso che la lezione si attenga meglio attraverso il gioco pratico. Se il bambino di 6 anni è pronto a capire che cos'è una chiusura, allora è abbastanza grande per andare da solo. Suggerirei di incollare il codice in jsfiddle.net, spiegando un po 'e lasciandoli da soli a inventare una canzone unica. Il testo esplicativo di seguito è probabilmente più appropriato per un bambino di 10 anni.

 
function sing(person) {

    var firstPart = "There was " + person + " who swallowed ";

    var fly = function() {
        var creature = "a fly";
        var result = "Perhaps she'll die";
        alert(firstPart + creature + "\n" + result);
    };

    var spider = function() {
        var creature = "a spider";
        var result = "that wiggled and jiggled and tickled inside her";
        alert(firstPart + creature + "\n" + result);
    };

    var bird = function() {
        var creature = "a bird";
        var result = "How absurd!";
        alert(firstPart + creature + "\n" + result);
    };

    var cat = function() {
        var creature = "a cat";
        var result = "Imagine That!";
        alert(firstPart + creature + "\n" + result);
    };

    fly();
    spider();
    bird();
    cat();
}

var person="an old lady";

sing(person);

Istruzioni

DATA: i dati sono una raccolta di fatti. Può essere numeri, parole, misure, osservazioni o anche solo descrizioni di cose. Non puoi toccarlo, annusarlo o assaggiarlo. Puoi scriverlo, parlarlo e ascoltarlo. Puoi usarlo per creare toccare odori e sapori usando un computer. Può essere reso utile da un computer usando il codice.

CODICE: tutta la scrittura sopra è chiamata code . È scritto in JavaScript.

JAVASCRIPT: JavaScript è una lingua. Come l'inglese o il francese o il cinese sono le lingue. Esistono molte lingue comprese dai computer e da altri processori elettronici. Perché JavaScript sia compreso da un computer ha bisogno di un interprete. Immagina se un insegnante che parla solo russo venga ad insegnare la tua classe a scuola. Quando l'insegnante dice "все садятся", la classe non capirebbe. Ma per fortuna hai un alunno russo nella tua classe che dice a tutti che questo significa "tutti si siedono" - così tutti voi. La classe è come un computer e l'allievo russo è l'interprete. Per JavaScript l'interprete più comune è chiamato browser.

BROWSER: quando ti connetti a Internet su un computer, tablet o telefono per visitare un sito web, utilizzi un browser. Esempi che potresti conoscere sono Internet Explorer, Chrome, Firefox e Safari. Il browser può capire JavaScript e dire al computer cosa deve fare. Le istruzioni JavaScript sono chiamate funzioni.

FUNZIONE: una funzione in JavaScript è come una fabbrica. Potrebbe essere una piccola fabbrica con una sola macchina al suo interno. Oppure potrebbe contenere molte altre piccole fabbriche, ognuna con molte macchine che svolgono diversi lavori. In una vera e propria fabbrica di vestiti per la vita potresti avere delle risme di stoffa e di fili di filo che entrano e magliette e jeans che escono. La nostra fabbrica JavaScript elabora solo i dati, non può cucire, praticare un foro o fondere il metallo. Nei nostri dati di fabbrica JavaScript entra e i dati vengono fuori.

Tutta questa roba di dati sembra un po 'noiosa, ma è davvero molto interessante; potremmo avere una funzione che dice a un robot cosa preparare per cena. Diciamo che invito te e il tuo amico a casa mia. Ti piacciono le cosce di pollo, mi piacciono le salsicce, il tuo amico vuole sempre quello che vuoi e il mio amico non mangia carne.

Non ho tempo per fare shopping, quindi la funzione deve sapere cosa abbiamo in frigo per prendere decisioni. Ogni ingrediente ha un tempo di cottura diverso e vogliamo che tutto sia servito caldo dal robot allo stesso tempo. Abbiamo bisogno di fornire la funzione con i dati su ciò che ci piace, la funzione potrebbe 'parlare' al frigorifero e la funzione potrebbe controllare il robot.

Una funzione normalmente ha un nome, parentesi e parentesi graffe. In questo modo:

 
function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

Notare che il codice di blocco /*...*/ e // viene letto dal browser.

NAME: puoi chiamare una funzione qualunque sia la parola che vuoi. L'esempio "cookMeal" è tipico nell'unire due parole insieme e dare alla seconda una lettera maiuscola all'inizio, ma questo non è necessario. Non può contenere uno spazio e non può essere un numero a sé stante.

PARENTHESES: "Parentheses" o () sono la buca delle lettere sulla porta della factory delle funzioni JavaScript o una casella postale in strada per l'invio di pacchetti di informazioni alla fabbrica. A volte la casella postale potrebbe essere contrassegnata per esempio cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime), nel qual caso sai quali dati devi fornire.

BRACES: "Bretelle" che assomigliano a questo {} sono le finestre colorate della nostra fabbrica. Dall'interno della fabbrica puoi vedere, ma dall'esterno non puoi vedere.

L'ESEMPIO DI CODICE LUNGO SOPRA

Il nostro codice inizia con la parola funzione , quindi sappiamo che è uno! Quindi il nome della funzione sing - questa è la mia descrizione personale di cosa tratta la funzione. Quindi parentesi () . Le parentesi sono sempre lì per una funzione. A volte sono vuoti, ea volte hanno qualcosa in. Questo ha una parola in: (person). Dopo questo c'è una coppia come questa {. Questo segna l'inizio della funzione sing () . Ha un partner che segna la fine di sing () come questo }

 
function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }

Quindi questa funzione potrebbe avere qualcosa a che fare con il canto e potrebbe aver bisogno di alcuni dati su una persona. Ha istruzioni interne per fare qualcosa con quei dati.

Now, dopo la funzione sing () , vicino alla fine del codice c'è la linea

 
var person="an old lady";

VARIABLE: le lettere var indicano "variabile". Una variabile è come una busta. All'esterno questa busta è contrassegnata come "persona". All'interno contiene un foglietto di carta con le informazioni di cui la nostra funzione ha bisogno, alcune lettere e spazi uniti come un pezzo di corda (si chiama una stringa) che fa una frase che legge "una vecchia signora". La nostra busta potrebbe contenere altri tipi di cose come numeri (chiamati interi), istruzioni (chiamate funzioni), liste (chiamate matrici ). Poiché questa variabile è scritta al di fuori di tutte le parentesi graffe {} e poiché è possibile vedere attraverso le finestre colorate quando ci si trova all'interno delle parentesi graffe, questa variabile può essere vista da qualsiasi parte del codice. La chiamiamo "variabile globale".

VARIABILE GLOBALE: persona è una variabile globale, il che significa che se cambi il suo valore da "una vecchia signora" a "un giovane uomo", la persona manterrà essere un giovane uomo finché non decidi di cambiarlo di nuovo e che qualsiasi altra funzione nel codice può vedere che è un giovane uomo. Premi il pulsante F12 o guarda le impostazioni di Opzioni per aprire la console di sviluppo di un browser e digita "persona" per vedere qual è questo valore. Digita person="a young man" per cambiarlo, quindi digita di nuovo "persona" per vedere che è cambiato.

Dopo questo abbiamo la linea

 
sing(person);

Questa linea chiama la funzione, come se stesse chiamando un cane

  

"Avanti canta ​​em>, vieni a ricevere persona !"

Quando il browser ha caricato il codice JavaScript e ha raggiunto questa linea, avvierà la funzione. Metto la linea alla fine per assicurarmi che il browser abbia tutte le informazioni necessarie per eseguirlo.

Le funzioni definiscono le azioni - la funzione principale riguarda il canto. Contiene una variabile chiamata firstPart che si applica al canto della persona che si applica a ciascuno dei versi della canzone: "C'era" + persona + "che inghiottì". Se digiti firstPart nella console, non otterrai una risposta perché la variabile è bloccata in una funzione - il browser non può vedere all'interno delle finestre colorate delle parentesi graffe.

CHIUSURE: le chiusure sono le funzioni più piccole che si trovano all'interno della grande funzione sing () . Le piccole fabbriche all'interno della grande fabbrica. Ognuno di loro ha le proprie parentesi, il che significa che le variabili al loro interno non possono essere viste dall'esterno. Ecco perché i nomi delle variabili ( creatura e risultato ) possono essere ripetuti nelle chiusure ma con valori diversi. Se digiti questi nomi di variabili nella finestra della console, non otterrai il suo valore perché è nascosto da due livelli di finestre colorate.

Le chiusure sanno tutte cosa è la variabile della funzione sing () chiamata firstPart , perché possono vedere dalle loro finestre colorate.

Dopo le chiusure arrivano le linee

 
fly();
spider();
bird();
cat();

La funzione sing () chiamerà ognuna di queste funzioni nell'ordine in cui vengono fornite. Quindi il lavoro della funzione sing () verrà eseguito.

    
59
2016-06-08 22: 11: 57Z

Va ​​bene, parlando con un bambino di 6 anni, potrei usare le seguenti associazioni.

  

Immagina - stai giocando con i tuoi fratellini e sorelline in tutta la casa, e ti muovi con i tuoi giocattoli e ne porti alcuni nella stanza di tuo fratello maggiore. Dopo un po 'tuo fratello tornò dalla scuola e andò nella sua stanza, e lui vi rinchiuse, così ora non potevi accedere ai giocattoli lasciati lì in modo diretto. Ma potresti bussare alla porta e chiedere a tuo fratello quei giocattoli. Si chiama chiusura del giocattolo ; tuo fratello ha inventato per te, e ora è in scope .

Confrontati con una situazione in cui una porta è stata bloccata da una brutta copia e nessuno all'interno (esecuzione di una funzione generale), quindi un incendio locale si verifica e brucia la stanza (garbage collector: D), e poi una nuova stanza è stata costruita e ora puoi lasciare altri giocattoli (nuova istanza di funzione), ma non ottenere mai gli stessi giocattoli che erano rimasti nella prima istanza della stanza.

Per un bambino avanzato vorrei mettere qualcosa come il seguente. Non è perfetto, ma ti fa sentire quello che è:

 
function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

Come puoi vedere, i giocattoli lasciati nella stanza sono ancora accessibili tramite il fratello e non importa se la stanza è chiusa. Ecco un jsbin per giocarci.

    
55
2014-10-25 22: 52: 13Z

Una risposta per un bambino di sei anni (ammesso che sappia cos'è una funzione e che cos'è una variabile e quali sono i dati):

Le funzioni possono restituire dati. Un tipo di dati che è possibile restituire da una funzione è un'altra funzione. Quando viene restituita la nuova funzione, tutte le variabili e gli argomenti utilizzati nella funzione che lo ha creato non scompaiono. Invece, quella funzione genitore "si chiude". In altre parole, niente può guardare al suo interno e vedere le variabili utilizzate tranne che per la funzione restituita. Quella nuova funzione ha una capacità speciale di guardare indietro all'interno della funzione che lo ha creato e vedere i dati al suo interno.

 
function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

Un altro modo molto semplice per spiegarlo è in termini di ambito:

Ogni volta che crei un ambito più piccolo all'interno di un ambito più ampio, l'ambito più piccolo sarà sempre in grado di vedere ciò che è nell'ambito più ampio.

    
50
2014-10-25 23: 02: 19Z
  1. Sembra che la chiusura sia equivalente a classi e classi interne in O.O.
    2017-02-15 06: 29: 45Z

Una funzione in JavaScript non è solo un riferimento a un insieme di istruzioni (come in linguaggio C), ma include anche una struttura di dati nascosta che è composta da riferimenti a tutte le variabili non locali che usa (variabili catturate). Tali funzioni a due pezzi sono chiamate chiusure. Ogni funzione in JavaScript può essere considerata una chiusura.

Le chiusure sono funzioni con uno stato. È in qualche modo simile a "questo" nel senso che "questo" fornisce anche lo stato per una funzione ma la funzione e "questo" sono oggetti separati ("questo" è solo un parametro di fantasia, e l'unico modo per legarlo in modo permanente ad un la funzione è creare una chiusura). Mentre "questa" e la funzione vivono sempre separatamente, una funzione non può essere separata dalla sua chiusura e il linguaggio non fornisce alcun mezzo per accedere alle variabili catturate.

Poiché tutte queste variabili esterne a cui fa riferimento una funzione annidata lessicamente sono in realtà variabili locali nella catena delle sue funzioni di inclusione lessicale (le variabili globali possono essere assunte come variabili locali di alcune funzioni di root) e ogni singola esecuzione di una funzione crea nuove istanze delle sue variabili locali, ne consegue che ogni esecuzione di una funzione che restituisce (o altrimenti lo trasferisce, come registrarla come callback) una funzione nidificata crea una nuova chiusura (con un proprio insieme potenzialmente unico di variabili non locali referenziate che rappresenta il suo contesto di esecuzione).

Inoltre, è necessario comprendere che le variabili locali in JavaScript non vengono create nello stack frame, ma nello heap e distrutte solo quando nessuno le sta facendo riferimento. Quando una funzione ritorna, i riferimenti alle sue variabili locali vengono decrementati, ma possono essere ancora non nulli se durante l'esecuzione corrente diventano parte di una chiusura e sono ancora referenziati dalle sue funzioni lessical nested (che possono accadere solo se i riferimenti a queste funzioni annidate sono state restituite o altrimenti trasferite su qualche codice esterno).

Un esempio:

 
function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();
    
49
2016-05-05 16: 04: 06Z

Forse un po 'oltre tutto, ma il più precoce dei bambini di sei anni, ma alcuni esempi che hanno aiutato a rendere il concetto di chiusura in JavaScript per me.

Una chiusura è una funzione che ha accesso all'ambito di un'altra funzione (le sue variabili e funzioni). Il modo più semplice per creare una chiusura è con una funzione all'interno di una funzione; il motivo è che in JavaScript una funzione ha sempre accesso all'ambito della sua funzione di contenimento.

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

ALERT: monkey

Nell'esempio precedente, viene chiamata externalFunction che a sua volta chiama innerFunction. Nota come outerVar è disponibile per innerFunction, evidenziato dal suo corretto avviso del valore di outerVar.

Ora considera quanto segue:

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

ALERT: monkey

referenceToInnerFunction è impostato su outerFunction (), che restituisce semplicemente ariferimento a innerFunction. Quando viene chiamata referenceToInnerFunction, restituisce outerVar. Di nuovo, come sopra, questo dimostra che innerFunction ha accesso a outerVar, una variabile di ExternalFunction. Inoltre, è interessante notare che mantiene questo accesso anche dopo che ExternalFunction ha terminato l'esecuzione.

E qui è dove le cose diventano davvero interessanti. Se dovessimo sbarazzarci di externalFunction, diciamo impostalo su null, potresti pensare che referenceToInnerFunction perderà il suo accesso al valore di outerVar. Ma questo non è il caso.

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());

ALERT: scimmia AVVISO: scimmia

Ma com'è questo? In che modo referenceToInnerFunction può ancora conoscere il valore di outerVar ora che externalFunction è stato impostato su null?

La ragione per cui referenceToInnerFunction può ancora accedere al valore di outerVar è perché quando la chiusura è stata creata ponendo innerFunction all'interno di outerFunction, innerFunction ha aggiunto un riferimento all'ambito di externalFunction (le sue variabili e funzioni) alla sua catena di scope. Ciò significa che innerFunction ha un puntatore o riferimento a tutte le variabili di ExternalFunction, incluso outerVar. Quindi, anche quando outerFunction ha terminato l'esecuzione, o anche se è cancellato o impostato su null, le variabili nel suo ambito, come outerVar, rimangono in memoria a causa del riferimento in sospeso a loro sulla parte della innerFunction che è stata restituita a referenceToInnerFunction. Per rilasciare veramente outerVar e il resto delle variabili di External function dalla memoria, dovresti eliminare questo riferimento eccezionale, ad esempio impostando referenceToInnerFunction anche su null.

//////////

Due altre cose sulle chiusure da notare. Innanzitutto, la chiusura avrà sempre accesso agli ultimi valori della sua funzione di contenimento.

 
function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();

ALERT: gorilla

In secondo luogo, quando viene creata una chiusura, mantiene un riferimento a tutte le sue variabili e funzioni della funzione di chiusura; non arriva a scegliere. E tuttavia, le chiusure dovrebbero essere usate con parsimonia, o almeno con attenzione, dato che possono essere ad uso intensivo di memoria; un sacco di variabili possono essere conservate in memoria molto tempo dopo che una funzione di contenimento ha terminato l'esecuzione.

    
48
2015-04-29 15: 37: 06Z

Indicerei semplicemente la pagina Mozilla Closures . È la migliore, più spiegazione concisa e semplice delle basi di chiusura e dell'uso pratico che ho trovato. È altamente raccomandato a chiunque stia imparando JavaScript.

E sì, lo consiglierei anche a un bambino di 6 anni - se il bambino di 6 anni sta imparando a fare chiusure, allora è logico che siano pronti a comprendere la spiegazione concisa e semplice fornito nell'articolo.

    
45
2014-10-25 22: 54: 15Z
  1. Sono d'accordo: la suddetta pagina di Mozilla è particolarmente semplice e concisa. Sorprendentemente il tuo post non è stato così apprezzato come gli altri.
    2018-04-28 08: 21: 59Z
fonte posta Qui
Altre domande