118 votes

Comment passer la valeur (et non la référence) d'une variable JS à une fonction ?

Voici une version simplifiée de ce que j'essaie d'exécuter :

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
        change_selection(i);
    }); 
}

mais je constate que chaque écouteur utilise la valeur de results.length (la valeur lorsque la boucle for se termine). Comment puis-je ajouter des listeners de telle sorte que chacun utilise la valeur de i au moment où je l'ajoute, plutôt que la référence à i ?

161voto

Andy E Points 132925

Dans les navigateurs modernes, vous pouvez utiliser l'option let o const pour créer une variable à l'échelle du bloc :

for (let i = 0; i < results.length; i++) {
  let marker = results[i];
  google.maps.event.addListener(marker, 'click', () => change_selection(i));
}

Dans les navigateurs plus anciens, vous devez créer une portée distincte qui enregistre la variable dans son état actuel en la passant comme paramètre de fonction :

for (var i = 0; i < results.length; i++) {
  (function (i) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
      change_selection(i);
    }); 
  })(i);
}

En créant une fonction anonyme et en l'appelant avec la variable comme premier argument, vous passez par valeur à la fonction et créez une fermeture.

4 votes

Vous voudrez ajouter var avant marker pour ne pas polluer l'espace de nom global.

2 votes

@ThiefMaster : étrangement, je viens de penser la même chose après avoir regardé cette réponse pour la première fois depuis un moment. Cependant, en regardant le code de l'OP, nous ne pouvons pas être entièrement sûrs que marker n'est pas déjà une variable globale.

0 votes

Ayant utilisé l'API de carte de Google, nous pouvons parier que la portée du marqueur est en dehors de la boucle for. Bien vu, Andy.

35voto

bobince Points 270740

En plus des fermetures, vous pouvez utiliser function.bind :

google.maps.event.addListener(marker, 'click', change_selection.bind(null, i));

transmet la valeur de i en tant qu'argument de la fonction lorsqu'elle est appelée. ( null est pour la liaison this dont vous n'avez pas besoin dans ce cas).

function.bind a été introduit par le cadre Prototype et a été normalisé dans la cinquième édition d'ECMAScript. Jusqu'à ce que tous les navigateurs le prennent en charge de manière native, vous pouvez ajouter votre propre code d'accès. function.bind l'utilisation de fermetures :

if (!('bind' in Function.prototype)) {
    Function.prototype.bind= function(owner) {
        var that= this;
        var args= Array.prototype.slice.call(arguments, 1);
        return function() {
            return that.apply(owner,
                args.length===0? arguments : arguments.length===0? args :
                args.concat(Array.prototype.slice.call(arguments, 0))
            );
        };
    };
}

2 votes

Je viens de remarquer ça, +1. Je suis un peu un fan de bind et j'ai hâte que les implémentations natives soient déployées.

0 votes

Quels sont les navigateurs qui prennent en charge cette fonction ? Des navigateurs mobiles ?

2 votes

@NoBugs : actuellement : IE9+. Fx4+, versions récentes de Chrome et Opera. Pas Safari, pas iPhone, le navigateur Android l'a depuis Ice Cream Sandwich.

13voto

David Murdoch Points 28521

Les fermetures :

for (var i = 0, l= results.length; i < l; i++) {
    marker = results[i];
    (function(index){
        google.maps.event.addListener(marker, 'click', function() { 
            change_selection(index);
        }); 
    })(i);
}

EDIT, 2013 : Ceux-ci sont désormais communément appelés IIFE

0 votes

Rien mauvais ici, mais -1 juste parce qu'Andy E y est arrivé le premier avec plus d'explications ; cette réponse n'ajoute rien à la page telle qu'elle est.

4 votes

Je ne suis pas sûr que vous compreniez les raisons du downvoting. Et cette réponse ajoute des informations en plus de la (excellente) réponse d'Andy : IIFE.

2voto

ajm Points 8631

Vous vous retrouvez avec une fermeture. Voici un article sur les fermetures et comment travailler avec eux. Consultez l'exemple 5 sur la page ; c'est le scénario auquel vous êtes confronté.

EDIT : Quatre ans plus tard, ce lien est mort. La racine de la question ci-dessus est que les for La boucle forme des fermetures (spécifiquement sur marker = results[i] ). Comme marker est passé dans addEventListener vous voyez l'effet secondaire de la fermeture : l'"environnement" partagé est mis à jour à chaque itération de la boucle, avant d'être finalement "sauvegardé" via la fermeture après la dernière itération. MDN explique cela très bien.

-2voto

user3647947 Points 1
for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', (function(i) {
        return function(){
            change_selection(i);
        }
    })(i)); 
}

7 votes

Ce serait une meilleure réponse si vous expliquiez pourquoi cela fonctionne.

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