De NoJS à MEAN, par où commencer ? Part II : “use strict” et self-invoking closure

Voilà un article que j’ai dans les tuyaux depuis 3 semaines car je n’arrivais pas à reformuler certaines de mes phrases. J’ai décidé de le publier en l’état. Faites moi savoir dans les commentaires si certaines choses ne sont pas compréhensibles.

Vous avez certainement déjà vu du code Javascript qui commençait par

“use strict”;

ou alors une fonction englobante comme celle-ci, qui est une self-invoking closure :

(function(){
  // CODE
})();

Si vous n’avez jamais vu cela ou si vous ne comprenez pas à quoi cela sert précisement, ce post est pour vous.

“use strict”;

Post-EDIT: Un de mes collègues m’a fait remarqué qu’il n’était pas recommandé d’utiliser “use strict”; dans ses modules, ceci car ES6 rend tous les modules strictes. Big Up François 🙂

“use strict”; a différents usages. En node il est utile afin que l’interpréteur détecte l’usage prématuré de variables. Côté client il est utile pour ne pas qu’une variable se retrouve involontairement dans le scope global. Je citerai un peu plus exhaustivement son usage après les exemples.

Dans la console (F12) de votre navigateur, nous allons initialiser une variable dans un scope réduit (dans une fonction) :

function foo(){
  bar=2
};
foo();

Vous remarquez qu’aucune erreur n’a été émise alors que nous n’avons pas déclaré cette variable, nous l’avons initialisée seulement. Maintenant faites :

console.log(bar);

“bar” vaut bien 2, alors que cette valeur n’est normalement pas sortie de la fonction “foo” (ni return, ni callback). Pourtant le schéma de mon précédent post est vrai, même dans ce cas ! Que s’est-il passé ?

Vous venez de déclarer la variable à une endroit bien différent de là où vous l’avez initialisée : Dans le Global Scope ! La variable “foo” est désormais accessible partout sur votre page web. Je pense que vous mesurez la portée négative de cette pratique.

Rafraichissez votre page pour réinitialiser le scope et exécuter cela dans votre console :

function foo(){
  "use strict";
  bar=2
};
foo();
console.log(bar);

Désormais votre navigateur vous indique qu’il ne peut pas exécuter ce code. Dans le cas de Firefox, vous obtiendrez l’erreur “ReferenceError: assignment to undeclared variable bar”.

Autant, côté client, dans votre navigateur, l’usage de “use strict;” peut sembler facultatif si vous souhaitez volontairement utiliser le Global Scope (bouuuhhh!). SAUF si vous utilisez un framework de type AngularJS qui utilise un service qui fournit la variable $rootScope, qui correspond au global scope de l’application AngularJS. Ainsi, si 2 applications AngularJS cohabitent, elles ne peuvent pas créer d’effets de bords l’une dans l’autre avec l’usage de “use strict;” et de $rootScope (en tant que scope le plus haut, car le global scope de votre page est encore plus haut).

Cependant, de manière générale, il vaut mieux éviter l’usage du scope global en Javascript ou de $rootScope dans AngularJS (et favoriser la création d’un service chargé de partager des variables entre vos contrôleurs), afin de s’épargner de désagréables surprises.

Si pour une raison X ou Y c’est une nécessité pour vous d’utiliser le global scope, alors favorisez l’usage de design pattern de type “namespacing” comme je l’ai décrit dans un de mes précédents posts.

En plus de cela :

  • “Use strict”; permet de rendre les stacktrace plus verbeuses car son usage permet de repérer les erreurs en amont de l’exécution. A l’inverse son absence risque de faire échouer l’exécution silencieusement.
  • Il supprime la création forcée de propriété dans l’objet this en provoquant une erreur lorsque vous tentez d’accéder à une propriété qui n’existe pas. Ex:
(function(){
  // "use strict"; //à décommenter pour l’exemple
  console.log(this.a);
})();
  • “use strict”; empêche les doublons parmis les arguments. Ex :
(function(){
  // "use strict"; //à décommenter pour l’exemple
  function foo(a,b,a){};
})();
  • Il empêche l’accès à une fonction ou une variable créée dans une fonction eval() au reste du scope.
(function(){
  var a = 2;
  eval('"use strict"; var a = 3'); //supprimez le “use strict”; pour l’exemple inverse
  console.log(a);
})();

J’attire cependant votre attention sur le fait que le “use strict”; doit être appliqué à l’intérieur de la fonction eval dans ce cas précis.

  • L’usage de “use strict”; permet de throw une erreur si vous tentez de supprimer une propriété d’un objet qui n’est pas configurable. En dehors de la déclaration de propriété dans un objet littéral, il est possible d’utiliser un décorateur du prototype Object pour gérer plus finement les propriétés (voir mon post sur les design pattern pour en savoir plus sur les décorateurs). Ex :
(function(){
  //"use strict"; //à décommenter pour l’exemple
  var obj = {}
  Object.defineProperty(obj, 'foo', {
    value: 'bar',
    configurable: false
  });
  console.log(obj.foo);
  delete obj.foo; // throw si use strict, sinon fail silent
  console.log(obj.foo); // Il est toujours là
})();

… et j’en oublis peut-être

self-invoking closure

Il s’agit d’une fonction d’encapsulation. Si vous vous souvenez, dans mon post précédent, un schéma vous montrait que la porté d’une variable se limitait à la fonction où elle était déclarée. La self-invoking closure permet de limiter le scope d’une variable afin de l’empêcher d’atteindre de global scope, ce qui pourrait provoquer des effets de bord non désirés. Son usage est largement facultatif coté serveur (avec node), car la fonction “require()” de node modularise systématiquement les scripts qu’elle importe, elle ajoute donc elle même une fonction englobante auto-invoquée.

var foo = ‘bar’;

foo sera accessible partout si elle n’est pas déclaré dans une fonction : Elle sera dans le Global Scope.

Alors que

(function(){
  var foo = ’bar’;
})();

limitera la portée de foo au scope de la fonction exécutée au moment de la lecture du script.

Je me suis servi de cela dans quasiment tous mes exemples car si vous les exécutez dans la console de votre navigateur, le résultat risque d’être tout à fait différent si vous vous en êtes passé.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s