Création de fermetures dans une boucle for – est-ce bien ce que je fais?

Je suis prob. être assez dense ici mais je ne peux pas comprendre exactement ce qui se passe dans le code ci-dessous.

Ce que j’essaie de faire est d’attacher deux gestionnaires distincts à l’événement de modification d’un champ. Chaque gestionnaire est configuré en effectuant une boucle sur un tableau et en utilisant les éléments du tableau pour générer la sortie du gestionnaire lorsqu’il s’exécute;

Le code suit:

$(document).ready( function () { // // Create some test input fields on the page... // $('
').insertAfter($('body > *:last')); $('').insertAfter($('body > *:last')); $('').insertAfter($('body > *:last')); // // The problematic part - for me at least... // var arr = new Array(1, 2); for (var a in arr) { // Using Chrome console here for logging console.log("## " + a); $('#t0').change(function () { console.log(">> " + a) }); } });

Donc, ce à quoi je m’attendrais lorsque j’ajoute une valeur au premier champ, à partir de la console (à l’aide de ces exemples dans Chrome):

 ## 0 ## 1 >> 1 >> 2 

Ce que je reçois c’est:

 ## 0 ## 1 >> 1 >> 1 

J’aurais pensé que la fonction transmise au gestionnaire formerait une fermeture sur la valeur de a et aboutirait à deux fonctions liées au gestionnaire, l’une dans laquelle a avait la valeur 1 et l’autre dans laquelle a avait la valeur 2 .

Des idées?

À la vôtre – kris

Il y a deux grosses erreurs ici:

Tout d’abord, le for (a in x) ne fonctionne pas comme prévu: il itère sur les propriétés de l’object, pas sur les éléments de tableau.

L’autre erreur est que a change au moment où la fonction est appelée. Voici un bon moyen d’atteindre la fonctionnalité désirée:

 for(var a=0; a 

Pour voir ce qui se passe avec la boucle for si vous ne créez pas de fermeture, voir ceci:

 var arr = [1,2,3]; var functions = []; for(var a=0; a 

Maintenant, toutes les fonctions vont log 3 , qui est l’indice du dernier élément du tableau + 1 ( arr[0] == 1, arr[1] == 2, arr[2] == 3 ). En a == arr.length la boucle for crée ces fonctions à chaque itération, mais elles sont exécutées à la fin de la boucle, quand a == arr.length .

Au lieu de la boucle “for”, essayez le propre outil de jQuery.

  $(arr).each(function(a) { console.log("## " + a); $('#t0').change(function () { console.log(">> " + a) }); }); 

Cela fonctionne comme prévu.

La raison pour laquelle votre code ne fonctionne pas est que les fermetures utilisent les valeurs les plus récentes de leurs variables. Autrement dit, si vous créez une fermeture avec a égal à 1, puis en créez une autre quand a égal à 2, les deux fermetures utiliseront la dernière valeur, 2. Ceci est évidemment déroutant, mais voici comment cela fonctionne.

Vous devez faire quelque chose comme ça:

 for (var a in arr) { (function (a) { // thanks to closure variable a is local here $('#t0').change(function () { console.log(">> " + a); }); }(a)); } 

ou comme ceci:

 for (var a in arr) { $('#t0').change((function (a) { return function () { // returned function has access to local variable a from // outer function console.log(">> " + a); }; }(a)); } 

Le truc est de mettre la variable a dans une scope plus profonde que pour la boucle. Pour être plus facile à lire, deux extraits ci-dessus pourraient être écrits comme suit:

 for (var a in arr) { (function (inner_a) { // thanks to closure variable a is local here $('#t0').change(function () { console.log(">> " + inner_a); }); }(a)); } for (var a in arr) { $('#t0').change((function (inner_a) { return function () { // returned function has access to local variable a from // outer function console.log(">> " + inner_a); }; }(a)); } 

BTW. il serait préférable d’utiliser classic for (var i …) au lieu de for .. in loop sauf si vous utilisez des propriétés d’object. Et $ (‘# t0’) devrait être mis en cache:

 var cachedEl = $('#t0'); for (var a=0; a < arr.length; a++) { (function (inner_a) { // thanks to closure variable a is local here cachedEl.change(function () { console.log(">> " + inner_a); }); }(a)); } // or for (var a=0; a < arr.length; a++) { cachedEl.change((function (inner_a) { return function () { // returned function has access to local variable a from // outer function console.log(">> " + inner_a); }; }(a)); }