Fermetures dans une boucle et environnement lexical

Cas simple: je souhaite charger plusieurs images qui ont un nom commun et un suffixe, par exemple: image0.png, image1.png, image2.png … imageN.png

J'utilise une boucle simple pour:

var images = []; for (var i=1; i<N; i++) { images[i] = new Image(); images[i].onload = function () { console.log("Image " + i + " loaded"); }; images[i].src = "image" + i + ".png"; } 

Ce que je reçois dans la console est:

 Image N loaded Image N loaded Image N loaded ... Image N loaded 

Mais ce que je veux, c'est comme ceci:

 Image 0 loaded Image 1 loaded Image 2 loaded ... Image N loaded 

Pourquoi cela arrive-t-il? Comment puis-je obtenir mon comportement désiré?

Le i dans votre fonction est évalué lorsque la fonction est exécutée , et non pas lorsque vous l'affectez à la charge. Votre boucle for a déjà terminée au moment où toutes les fonctions de votre charge sont onload , de sorte que toutes voyez la valeur finale N

Pour capturer la valeur actuelle de i , vous devez la passer comme paramètre à une autre fonction où elle peut être captée en tant que variable locale:

 function captureI(i) { return function () { console.log("Image " + i + " loaded"); }; } var images = []; for (var i=1; i<N; i++) { images[i] = new Image(); images[i].onload = captureI(i); images[i].src = "image" + i + ".png"; } 

Cela fonctionne car chaque fois que vous appelez captureI , une nouvelle variable locale est créée pour cette instance de captureI . Essentiellement, vous créez N variables différentes et chaque fonction onload capture une instance différente de la variable.

Vous pouvez l'envelopper dans une fermeture pour éviter d'utiliser la variable i , ce qui est une variable de boucle et modifie donc:

 (function(j) { images[i].onload = function () { console.log("Image " + i + ", " + j + " loaded"); }; })(i); 

Cela démontre la différence entre i , qui est une variable de boucle et change, et j , qui est un paramètre lié à la fonction, qui ne change pas.

Voir le jsfiddle ici:

Votre variable de compteur de boucle a déjà été écrasée. Consultez cette rubrique FAQ expliquant exactement pourquoi il se passe et comment contourner ce problème.

Comme la variable i est déclarée hors du cadre de la boucle, elle conserve sa valeur finale après la fin de la boucle. Les fonctions anonymes que vous créez, alors toutes se lient à cette variable, et lorsqu'elles sont appelées, elles obtiennent toutes la même valeur finale de N

Il y a une bonne discussion dans cette question .