Problema di loop infame Javascript?

Ho il seguente frammento di codice.

function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function () { alert(i); }; document.body.appendChild(link); } } 

Il codice sopra riportato è per la generazione di 5 link e associa ogni link a un evento di avviso per mostrare l’id del link corrente. Ma non funziona. Quando fai clic sui link generati, tutti dicono “link 5”.

Ma il seguente frammento di codice funziona come le nostre aspettative.

 function addLinks () { for (var i=0, link; i<5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; link.onclick = function (num) { return function () { alert(num); }; }(i); document.body.appendChild(link); } } 

I suddetti 2 frammenti sono citati da qui . Come la spiegazione dell’autore, sembra che la chiusura faccia la magia.

Ma come funziona e come la chiusura lo fa funzionare sono al di fuori della mia comprensione. Perché il primo non funziona mentre il secondo funziona? Qualcuno può dare una spiegazione dettagliata della magia?

Grazie.

Citando me stesso per una spiegazione del primo esempio:

Gli ambiti JavaScript sono a livello di funzione, non a livello di blocco, e la creazione di una chiusura significa semplicemente che l’ambito che racchiude viene aggiunto all’ambiente lessicale della funzione chiusa.

Dopo che il ciclo termina, la variabile a livello di funzione i ha il valore 5, ed è ciò che la funzione interna ‘vede’.

Nel secondo esempio, per ogni fase di iterazione la funzione esterna letterale valuterà un nuovo object funzione con il proprio ambito e la variabile locale num , il cui valore è impostato sul valore corrente di i . Dato che num non viene mai modificato, rimarrà costante per tutta la durata della chiusura: Il prossimo passo di iterazione non sovrascrive il vecchio valore poiché gli oggetti funzione sono indipendenti.

Tieni presente che questo approccio è piuttosto inefficiente poiché per ogni collegamento devono essere creati due nuovi oggetti funzione. Questo non è necessario, poiché possono essere facilmente condivisi se si utilizza il nodo DOM per l’archiviazione delle informazioni:

 function linkListener() { alert(this.i); } function addLinks () { for(var i = 0; i < 5; ++i) { var link = document.createElement('a'); link.appendChild(document.createTextNode('Link ' + i)); link.i = i; link.onclick = linkListener; document.body.appendChild(link); } } 

Mi piace scrivere semplici spiegazioni per gente spessa, perché sono spesso così qui va …

Abbiamo 5 div sulla pagina, ognuno con un ID … div1, div2, div3, div4, div5

jQuery può farlo …

 for (var i=1; i< =5; i++) { $("#div" + i).click ( function() { alert ($(this).index()) } ) } 

Ma davvero affrontando il problema (e costruendo questo lentamente) ...

PASSO 1

 for (var i=1; i< =5; i++) { $("#div" + i).click ( // TODO: Write function to handle click event ) } 

PASSO 2

 for (var i=1; i< =5; i++) { $("#div" + i).click ( function(num) { // A functions variable values are set WHEN THE FUNCTION IS CALLED! // PLEASE UNDERSTAND THIS AND YOU ARE HOME AND DRY (took me 2 years)! // Now the click event is expecting a function as a handler so return it return function() { alert (num) } }(i) // We call the function here, passing in i ) } 

SEMPLICE DA COMPRENDERE ALTERNATIVO

Se non riesci a capirlo, questo dovrebbe essere più facile da capire e ha lo stesso effetto ...

 for (var i=1; i< =5; i++) { function clickHandler(num) { $("#div" + i).click ( function() { alert (num) } ) } clickHandler(i); } 

Questo dovrebbe essere semplice da capire se ricordi che i valori di una variabile di funzioni sono impostati quando viene chiamata la funzione (ma questo usa lo stesso identico processo di pensiero di prima)

Fondamentalmente, nel primo esempio si sta vincolando l’ i all’interno del gestore onclick direttamente al file i al di fuori del gestore onclick . Quindi, quando l’ i al di fuori del gestore onclick cambia, anche l’ i all’interno del gestore onclick cambia.

Nel secondo esempio, invece di legarlo al num nel gestore onclick , lo si passa in una funzione, che quindi la lega al num nel gestore onclick . Quando lo passi nella funzione, il valore di i viene copiato, non associato a num . Quindi quando cambio, num rimane lo stesso. La copia si verifica perché le funzioni in JavaScript sono “chiusure”, il che significa che una volta che qualcosa viene passato alla funzione, è “chiuso” per la modifica esterna.

Altri hanno spiegato cosa sta succedendo, ecco una soluzione alternativa.

 function addLinks () { for (var i = 0, link; i < 5; i++) { link = document.createElement("a"); link.innerHTML = "Link " + i; with ({ n: i }) { link.onclick = function() { alert(n); }; } document.body.appendChild(link); } } 

Fondamentalmente, il povero uomo lascia bind.

Nel primo esempio, si collega semplicemente questa funzione all’evento onclick:

 function() {alert(i);}; 

Ciò significa che nell’evento click js deve essere avvertito il valore della funzione addlink i variabile. Il suo valore sarà 5 a causa del ciclo for ().

Nel secondo esempio si genera una funzione da associare a un’altra funzione:

 function (num) { return function () { alert(num); }; } 

Ciò significa: se chiamato con un valore, restituiscimi una funzione che avviserà il valore di input. Ad es. La function(3) chiamata function(3) restituirà function() { alert(3) }; .

Si chiama questa funzione con il valore i ad ogni iterazione, quindi si creano funzioni onclick separate per ciascun collegamento.

Il punto è che nel primo esempio la tua funzione conteneva un riferimento variabile, mentre nel secondo con l’aiuto della funzione esterna hai sostituito il riferimento con un valore reale. Questa è chiamata chiusura quasi perché “racchiudi” il valore corrente di una variabile all’interno della tua funzione invece di mantenere un riferimento ad essa.