La fameuse Boucle Javascript? [dupliquer]

cette question a déjà une réponse ici:

j'ai le code suivant.

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

le code ci-dessus est pour générer 5 liens et lier chaque lien avec un événement d'alerte pour afficher l'id du lien courant. Mais Ça ne fonctionne pas. Lorsque vous cliquez sur les liens générés, ils disent tous "lien 5".

mais les codes suivants fonctionnent comme notre attente.

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

les 2 extraits ci-dessus sont cités de ici . Comme l'explique l'auteur, semble le fermeture fait la magie.

mais comment cela fonctionne et comment fermeture qui sont tous au-delà de ma compréhension. Pourquoi le premier ne fonctionne pas, alors que le second fonctionne? Quelqu'un peut-il donner une explication détaillée de la magie?

merci.

206
demandé sur Taryn 2009-09-20 17:21:54

5 réponses

me citant pour une explication du premier exemple:

les portées JavaScript sont au niveau de la fonction, pas au niveau du bloc, et la création d'une fermeture signifie simplement que la portée enveloppante est ajoutée à l'environnement lexical de la fonction incluse.

après la boucle se termine, la variable de niveau de fonction i a la valeur 5, et c'est ce que la fonction interne 'voit'.

inefficace car deux nouveaux objets de fonction doivent être créés pour chaque lien. Cela n'est pas nécessaire, car ils peuvent facilement être partagés si vous utilisez le noeud DOM pour le stockage de l'information:

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);
    }
}
102
répondu Christoph 2017-05-23 12:34:08

j'aime écrire des explications simples pour les gens épais, parce que je suis épais alors voilà ...

nous avons 5 divs sur la page, chacun avec un ID ... div1, div2, div3, div4, div5

jQuery peut le faire ...

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

mais en abordant vraiment le problème (et en le construisant lentement) ...

STEP 1

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

STEP 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
    )
}

SIMPLE À COMPRENDRE ALTERNATIVE

si vous ne pouvez pas avoir votre tête autour de cela, alors cela devrait être plus facile à comprendre et a le même effet ...

for (var i=1; i<=5; i++) {

    function clickHandler(num) {    
        $("#div" + i).click (
            function() { alert (num) }
        )
    }
    clickHandler(i);

}

cela devrait être simple à comprendre si vous vous souvenez qu'une fonction variable valeurs sont définies lorsque la fonction est appelée (mais cela utilise le même processus de pensée exacte qu'avant)

76
répondu Daniel Lewis 2012-01-29 16:37:21

en gros, dans le premier exemple, vous liez le i à l'intérieur du handler onclick directement au i à l'extérieur du handler onclick . Ainsi, lorsque le manipulateur i à l'extérieur du manipulateur onclick change, le manipulateur i à l'intérieur du manipulateur onclick change aussi.

dans le second exemple, au lieu de le lier au num dans le handler onclick , vous le passez dans une fonction, qui le lie ensuite au num dans le onclick gestionnaire. Lorsque vous le passez dans la fonction, la valeur de i est copié, non lié à num . Donc quand i change, num reste le même. La copie se produit parce que les fonctions en JavaScript sont des "fermetures", ce qui signifie qu'une fois que quelque chose est passé dans la fonction, il est" fermé " pour la modification externe.

19
répondu Imagist 2009-09-20 13:46:38

D'autres ont expliqué ce qui se passe, voici une solution alternative.

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

essentiellement les pauvres hommes let-binding.

17
répondu nlogax 2009-09-26 21:38:22

dans le premier exemple, vous liez simplement cette fonction à l'événement onclick:

function() {alert(i);};

Cela signifie que sur l'événement click js devrait alerter la valeur de la addlink fonctions d'une variable. Sa valeur sera 5 à cause de la boucle for().

dans le deuxième exemple, vous générez une fonction à lier avec une autre fonction:

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

cela signifie: Si appelé avec une valeur, me rendre une fonction qui va alerter la valeur d'entrée. Par exemple: en appelant function(3) , vous obtiendrez function() { alert(3) }; .

vous appelez cette fonction avec la valeur i à chaque itération, donc vous créez des fonctions onclick séparées pour chaque lien.

le point est que dans le premier exemple votre fonction contenait une référence variable, tandis que dans le second avec l'aide de la fonction externe vous substitué la référence avec une valeur réelle. Cela s'appelle une fermeture en gros parce que vous " enfermez" la valeur courante d'une variable dans votre fonction au lieu de garder une référence à elle.

5
répondu Zed 2009-09-20 13:59:07