Passer des fonctions à setTimeout en boucle: toujours la dernière valeur?

J'essaie d'utiliser setTimeout pour exécuter une fonction anonyme sur laquelle je transmet des informations et j'ai des problèmes. Cette (version codée) fonctionnerait très bien:

setTimeout(function(){alert("hello");},1000); setTimeout(function(){alert("world");},2000); 

Mais j'essaie de prendre le bonjour et du monde à partir d'un tableau et de les transférer dans la fonction sans (a) utiliser des variables globales, et (2) utiliser eval. Je sais comment je pourrais le faire en utilisant globals ou eval, mais comment puis-je le faire sans. Voici ce que j'aimerais faire (mais je sais que cela ne fonctionnera pas):

 var strings = [ "hello", "world" ]; var delay = 1000; for(var i=0;i<strings.length;i++) { setTimeout( function(){alert(strings[i]);}, delay); delay += 1000; } 

Bien sûr, les chaînes [i] seront hors contexte. Comment puis-je passer des chaînes [i] dans cette fonction anonyme sans eval ou globals?

C'est le problème très fréquent de «comment puis-je utiliser une variable de boucle dans une fermeture».

La solution canonique consiste à appeler une fonction qui renvoie une fonction qui est liée à la valeur actuelle de la variable de boucle:

 var strings = [ "hello", "world" ]; var delay = 1000; for(var i=0;i<strings.length;i++) { setTimeout( (function(s) { return function() { alert(s); } })(strings[i]), delay); delay += 1000; } 

La function(s) { ... } définition externe function(s) { ... } crée une nouvelle portée où s est lié à la valeur actuelle du paramètre fourni – c'est-à-dire des strings[i] – où il est disponible dans la portée interne .

Ajoutez simplement une portée autour de l'appel setTimeout:

 var strings = [ "hello", "world" ]; var delay = 1000; for(var i=0;i<strings.length;i++) { (function(s){ setTimeout( function(){alert(s);}, delay); })(strings[i]); delay += 1000; } 

Vous pouvez écrire une fonction distincte pour configurer le délai d'attente:

 function doTimer(str, delay) { setTimeout(function() { alert(str); }, delay); } 

Ensuite, appelle cela de la boucle:

 var delay = 1000; for(var i=0;i<strings.length;i++) { doTimer(strings[i], delay); delay += 1000; } 

Bien que n'étant pas aussi compatible avec l'arrière que certaines des autres réponses, j'ai pensé que je lancerais une autre option … cette fois en utilisant bind () !

 var strings = [ "hello", "world" ]; var delay = 1000; for(var i=0;i<strings.length;i++) { setTimeout(alert.bind(this, strings[i]), delay); delay += 1000; } 

Voir la démo en action

 var strings = [ "hello", "world" ]; var delay = 1000; for(var i=0;i<strings.length;i++) { setTimeout( new Function('alert(strings[i]);'), delay); delay += 1000; }