227 votes

Tristement célèbre problème de boucle JavaScript ?

J'ai l'extrait de 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 la génération 5 liens et de lier chaque lien avec un événement d'alerte pour afficher le lien de l'id. Mais Ça ne fonctionne pas. Lorsque vous cliquez sur les liens générés, ils disent tous "lien 5".

Mais les codes suivants extrait fonctionne 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);
    }
}

Ci-dessus 2 extraits sont cités à partir d' ici. Comme l'auteur de l'explication semble la fermeture de fait de la magie.

Mais comment elle fonctionne et comment la fermeture permet de travailler 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.

107voto

Christoph Points 64389

Me citer pour une explication de la première exemple:

JavaScript étendues sont fonction du niveau, pas au niveau du bloc et de la création d'une fermeture signifie simplement que le cadre englobant est ajoutée à l'environnement lexical du clos de la fonction.

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

Dans le deuxième exemple, pour chaque étape de l'itération de la fonction externe littérale permettra d'évaluer à une nouvelle fonction de l'objet avec son propre champ d'application et la variable locale num, dont la valeur est définie à la valeur courante de i. En tant que num n'est jamais modifié, il va rester constante sur la durée de vie de la fermeture: La prochaine étape de l'itération n'est pas remplacer l'ancienne valeur que la fonction des objets sont indépendantes.

Gardez à l'esprit que cette approche est plutôt inefficace que deux nouvelles la fonction des objets doivent être créés pour chaque lien. Ce n'est pas nécessaire, car ils peuvent facilement être partagés si vous utilisez le nœud 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);
    }
}

80voto

Daniel Lewis Points 299

J'aime écrire des explications simples pour une épaisseur de gens, parce que je suis épais donc, ici, va ...

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 vraiment régler le problème (et la construction de ce lentement) ...

ÉTAPE 1

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

ÉTAPE 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 obtenir votre tête autour de ce que 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 de l'une des fonctions les valeurs des variables sont définies lorsque la fonction est appelée (mais il utilise exactement le même processus de pensée comme avant)

21voto

Imagist Points 5348

Pour l'essentiel, dans le premier exemple, vous êtes à la liaison de l' i à l'intérieur de l' onclick gestionnaire directement à l' i à l'extérieur de l' onclick gestionnaire. Ainsi, lorsque l' i à l'extérieur de l' onclick gestionnaire de changements, l' i à l'intérieur de l' onclick gestionnaire de trop de changements.

Dans le deuxième exemple, au lieu de qui le lie à l' num dans la onclick gestionnaire, vous êtes de passage dans une fonction, qui se lie alors à l' num dans la onclick gestionnaire. Lorsque vous transmettez à la fonction, la valeur de i est copié, qui n'est pas lié à l' num. Ainsi, lorsque i changements, num reste le même. La copie se passe 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 l'extérieur de la modification.

18voto

nlogax Points 942

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

Fondamentalement, le pauvre équipe let-liaison.

5voto

Zed Points 27408

Dans le premier exemple, il vous suffit de lier 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 de 5 à cause de la boucle for().

Dans le deuxième exemple vous générer une fonction à être lié avec une autre fonction:

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

Cela signifie: si la fonction est appelée avec une valeur de retour moi une fonction d'alerte de la valeur d'entrée. E. g. appelant function(3) sera de retour function() { alert(3) };.

Vous appelez cette fonction avec la valeur que j'ai à chaque itération, donc vous créer des fonctions onclick pour chaque liens.

Le point est que dans le premier exemple votre fonction contenue d'une variable de référence, tandis que dans le second, avec l'aide de la fonction externe vous substituée à la référence à une valeur réelle. Cela s'appelle une fermeture à peu près parce que vous "enfermer" la valeur actuelle d'une variable à l'intérieur de votre fonction au lieu de garder une référence à elle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X