Les closures en JavaScript /
La portée & accessibilitée des variables

29/02/2016

Vous connaissez surement la portée des variables en javascript. Elle est définie par le scope. En effet si une variable est définie à l'intérieure d'une fonction celle-ci n'est pas accessible à l'extérieur de cette même function. Cependant que se passe t'il quand une function retourne une function ? Les variables contenues dans cette fonction seront elles accessibles ? En utilisant les functions expression oui !

Closure / simple


function testClosure(){ var x = 4; return x; } testClosure(); // retourne 4 console.log(x) // retourne "undefined"

La variable x retourne « undefined » car elle à été définie à l’intérieur d’une fonction, elle n’est donc accessible uniquement dans la fonction dans laquelle elle a été définie.

Closure avec une fonction / retournant une function expression


function testClosure(){ var x = 4; function closeX(){ return x; } return closeX; }

En suivant la même logique que dans le point précédant, il n’est pas possible d’accéder depuis l’extérieur à la variable x.

Mais comme cette function retourne une function, lorsque l’on assigne son résultat à une variable. L’exécution de celle-ci nous retourne la valeur de x !


var checkLocal = testClosure(); checkLocal(); // retourne 4 testClosure()(); // retourne aussi 4

Paramètres multiples dans des closures

Mais si nous voulions insérer des paramètres avec une closure. Comment procéder ?



function buildCoverTicketMaker(transport){ return function(name){ alert("Here is your transportation ticket via the "+transport+".\n" + " Welcome to the Cold Coldclosur Cove "+name+" !"); }; }

Pour exécuter cette function nous avons donc deux solutions. La première avec la création d’une function expression.

var getSubmarineTicket = buildCoverTicketMaker('Battleship');

getSubmarineTicket('Mario');
// retourne
// Here is your transportation ticket via the Giant Seagull. Welcome to the Cold Coldclosur Cove Mario !

La deuxième avec les paramètres de chaque function dans des groupes de parenthèses individuelles.


buildCoverTicketMaker('Battleship')('Mario');

Attention aux erreurs !

Avec les closures il faut faire extrêmement attention au moment ou sont retourné les valeurs. spécialement dans les boucles car dans une closure le retour se fait au dernier moment possible.

Petite démo :



function assignTorpedo(name,passengerArray){ var torpedoAssignment; for(var i = 0; i < passengerArray.length; i++){ if(passengerArray[i] == name){ torpedoAssignment = function(){ alert(name+' You are in charge at torpedo #'+i); }; } } return torpedoAssignment; } var passengerArray = ["Batman","Superman","Homer","Smithers"] var giveAssignement = assignTorpedo('Homer',passengerArray); giveAssignement(); // retourne Homer You are in charge at torpedo #4 alors que homer est à la #2 :/

Cela retourne « Homer You are in charge at torpedo # 4 » alors que Homer est à la position 2 ! En fait le problème est que la boucle continue de tourner et donc la valeur de i avant de retourner torpedoAssignment

Pour éviter ce problème il ne faut pas attendre la fin de l’exécution de la boucle pour retourner la function

Il faut donc faire :

function assignTorpedo(name,passengerArray){
  for(var i = 0; i < passengerArray.length; i++){
    if(passengerArray[i] == name){
      return function(){
        alert(name+' You are in charge at torpedo #'+i);
      };
    }
  }
}

var passengerArray = ["Batman","Superman","Homer","Smithers"]
var giveAssignement = assignTorpedo('Homer',passengerArray);
giveAssignement();
// retourne Homer You are in charge at torpedo #2 (parfait !)

On pourrait aussi le faire avec la loop directement dans la function qui est retournée.


function assignTorpedo(name,passengerArray){ return function(){ for(var i = 0; i < passengerArray.length; i++){ if(passengerArray[i] == name){ alert(name+' You are in charge at torpedo #'+i); } } }; } var passengerArray = ["Batman","Superman","Homer","Smithers"] var giveAssignement = assignTorpedo('Homer',passengerArray); giveAssignement(); // retourne Homer You are in charge at torpedo #2 (parfait !)