debugging - working - remove google tag manager preview




Comment éviter l'accès variable variable de la fermeture (4)

J'ai du code comme celui-ci:

for(var id=0; id < message.receiver.length; id++){
   var tmp_id = id;
   zlib.gzip(JSON.stringify(message.json), function(err, buffer){
                        ...
   pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
   delete pushStatusPool[message.receiver[tmp_id]];
   ...
   });
}

Et j'ai eu un avertissement que l'utilisation de tmp_id en fermeture peut poser problème car c'est une variable mutable.

Comment pourrais-je éviter cela? Je veux dire, comment pourrais-je envoyer une variable immuable à callback car c'est une boucle for et je ne peux pas changer le code de zlib.gzip ? Ou en d'autres termes, comment pourrais-je transmettre un argument à une fermeture?


@ user24359 answer est une bonne solution mais vous pouvez simplement remplacer le mot-clé var par le mot let clé let .

for(var id=0;

devient

for(let id=0;

Voir les détails here .

Edit : Comme Heriberto Juárez l'a suggéré, cela ne fonctionnera que pour les navigateurs qui supportent EcmaScript6.


Créer des fermetures dans une boucle avec var ( tmp_id ) se trouvant dans la partie supérieure de la fonction de rappel est une erreur courante qui doit être évitée car la var n'est pas définie dans un bloc. De ce fait, et parce que chaque fermeture créée dans la boucle partage le même environnement lexical , la variable sera toujours la dernière valeur itérée (par exemple, message.receiver.length - 1 comme tmp_id ) lorsque la fonction de rappel est appelée. Votre IDE détecte ce comportement et se plaint à juste titre.

Pour éviter l'avertissement, il existe plusieurs solutions:

  • Remplacez var par let veillant à ce que chaque fermeture créée ait son propre tmp_id défini dans chaque itération:

    for (var id = 0; id < message.receiver.length; id++) {
      let tmp_id = id;
      zlib.gzip(JSON.stringify(message.json), function(err, buffer) {
        // Do something with tmp_id ...
      });
    }
    
  • Créez un environnement lexical à chaque itération en utilisant IIEF comme l'a fait gennadi.w .

  • Créez une fonction de rappel dans chaque itération en utilisant une fonction usine ( createCallback ):

    const createCallback = tmp_id => function(err, buffer) {
      // Do something with tmp_id ...
    };
    for (var id = 0; id < message.receiver.length; id++) {
      zlib.gzip(JSON.stringify(message.json), createCallback(id));
    }
    
  • bind la ou les variables sur la fonction de rappel dans laquelle elles sont ajoutées à leurs paramètres:

    for (var id = 0; id < message.receiver.length; id++) {
      zlib.gzip(JSON.stringify(message.json), function(tmp_id, err, buffer) {
        // Do something with tmp_id (passed as id) ...
      }.bind(this, id));
    }
    

Si possible, var doit être évité dès ECMAScript 2015 en raison de ces comportements prédisposés aux erreurs.


J'ai rencontré le même problème et l'ai résolu en modifiant légèrement la réponse de user24359, en passant l'id à la fermeture:

for(var id=0; id < message.receiver.length; id++){
   (function(tmp_id){
       zlib.gzip(JSON.stringify(message.json), function(err, buffer){
                        ...
           pushStatusPool[message.receiver[tmp_id]] = null; // fix memory leak
           delete pushStatusPool[message.receiver[tmp_id]];
           ...
       });
   })(id);
}

Voici une simplification de la réponse de user24359. Ceci est la solution:

var object = {a:1,b:2};

for (var y in object){
    (function(){const yyy = y;
        setTimeout(function(){console.log(yyy)},3000);})();
}

Le code ci-dessus enregistre ab et est la solution. Les journaux de code suivants bb:

var object = {a:1,b:2};
for (var y in object){

    setTimeout(function(){console.log(y)},3000);
}




closures