Le mode strict fut introduit avec ECMAScript 5 et est désormais présent dans les principaux navigateurs. Pour indiquer au navigateur l'utilisation de ce mode, il suffit d'ajouter "use strict";
au début du code source. En revanche, il faut un peu plus de travail afin de migrer une base de code existante afin qu'elle utilise intégralement le mode strict.
Cet article a pour but de guider les développeurs qui souhaitent effectuer cette migration.
Transition progressive
Le mode strict a été conçu afin que la transition puisse être effectuée de façon progressive. Il est possible de modifier chaque fichier individuellement voire, éventuellement, de descendre cette granularité aux fonctions.
Différences entre strict et non-strict
Erreurs de syntaxe
En utilisant "use strict";
, certaines instructions ou fragments de code lanceront une exception SyntaxError
avant l'exécution du script :
- La syntaxe pour la base octale :
var n = 023;
- L'instruction
with
- L'instruction
delete
pour un nom de variabledelete maVariable
; - L'utilisation de
eval()
ouarguments
comme un nom de variable ou un nom d'argument - L'utilisation d'un des nouveaux mots-clés réservés (en prévision d'ECMAScript 6) :
implements
,interface
,let
,package
,private
,protected
,public
,static
, etyield
- La déclaration de fonctions dans des blocs
if(a<b){ function f(){} }
- Les erreurs évidentes
- Déclarer deux fois le nom d'une propriété dans un littéral objet
{a: 1, b: 3, a: 7}
. Ceci n'est plus le cas pour ECMAScript 6 : bug 1041128 - Déclarer deux arguments de fonction avec le même nom
function f(a, b, b){}
- Déclarer deux fois le nom d'une propriété dans un littéral objet
Ces erreurs sont bienvenues car elles révèlent des mauvaises pratiques et certaines erreurs claires. Elles apparaissent avant l'exécution du code.
Erreurs à l'exécution
JavaScript échoue silencieusement dans certains contextes où une erreur se produit. Le mode strict lance une exception dans ces cas. Si votre code contient certains de ces cas, il sera nécessaire de faire des tests afin de vous assurer que rien n'est cassé. Encore une fois, il est possible d'utiliser le mode strict à la granularité des fonctions.
Attribuer une valeur à une variable non déclarée
function f(x){ "use strict"; var a = 12; b = a + x*35; // erreur ! } f(42);
Cela a pour effet de changer une valeur de l'objet global, ce qui est rarement voulu. Si vous souhaitez définir une valeur pour l'objet global, utilisez le comme argument et assignez la propriété de façon explicite :
// au niveau le plus haut "this" fait toujours référence // à l'objet global var global = this; function f(x){ "use strict"; var a = 12; global.b = a + x*35; } f(42);
Essayer de supprimer une propriété non-configurable
"use strict"; delete Object.prototype; // erreur !
En mode non-strict, cela serait passé sous silence (contrairement à ce à quoi l'utilisateur pourrait s'attendre).
Utiliser les mauvaises propriétés d'arguments et function
Utiliser arguments.callee
, arguments.caller
, anyFunction.caller
ou encore anyFunction.arguments
renverra une erreur en mode strict. Le seul cas légitime pour les utiliser serait :
// exemple tiré de vanillajs: https://vanilla-js.com/ var s = document.getElementById('truc').style; s.opacity = 1; (function(){ if((s.opacity-=.1) < 0) s.display="none"; else setTimeout(arguments.callee, 40); })();
qu'on peut réécrire en :
"use strict"; var s = document.getElementById('truc').style; s.opacity = 1; (function fadeOut(){ // on nomme la fonction if((s.opacity-=.1) < 0) s.display="none"; else setTimeout(fadeOut, 40); // on utilise ce nom })();
Les différences sémantiques
Ces différences sont très subtiles et il est possible qu'un ensemble de tests ne détecte pas ces différences. Il peut être alors nécessaire d'analyser votre code avec précaution afin la signification du code n'ait pas changé. Encore une fois, cela peut être fait fonction par fonction.
Le sens de this
dans les appels de fonction
Lors de l'appel à une fonction comme f()
, la valeur de this
correspondait à l'objet global. En mode strict, cette valeur devient undefined
. Lorsqu'une fonction était appelée avec call()
ou apply()
, si la valeur était une valeur primitive, elle était placée dans un objet (ou dans l'objet global pour undefined
et null
). En mode strict, la valeur est passée directement, sans conversion ni remplacement.
arguments
ne crée pas d'alias pour les arguments nommés d'une fonction
En mode non-strict, la modification d'une valeur de l'objet arguments
entraînait la modification de l'argument correspondant. Cela complexifie les optimisations des moteurs JavaScript et et la lecture du code. En mode strict, l'objet arguments
est créé et initialisé avec les mêmes valeurs que les arguments nommés. En revanche, les changements apportés à l'objet arguments
ou aux arguments nommés ne sont pas reproduit de l'un vers l'autre et réciproquement.
Changements apportés à eval
En mode strict, eval
ne crée pas de nouvelle variable dans la portée depuis laquelle il a été appelé. Bien entendu, la chaîne évaluée est évaluée selon les règles du mode strict. Pour s'assurer du bon fonctionnement de cette évaluation, il faut s'assurer des cas de figures qui s'y présentent pour les tester. Rappel : il ne faut utiliser eval
que si cela est nécessaire (les dangers liés à cette fonction font qu'on observe de mauvaises pratiques).
La neutralité du code quant au mode strict
Un des aspects négatifs de cette migration est la sémantique : le sens du code pourrait être différent dans les navigateurs historiques qui n'implémentent pas le mode strict. Dans quelques rares cas (une mauvaise concaténation ou minification), votre code pourrait ne pas fonctionner dans le mode que vous avez testé. Voici quelques règles pour que le code soit le plus neutre possible quant au mode choisi (strict ou non-strict) :
- Écrivez votre code « strictement » et assurez vous de lancer des exceptions dans le cadre d'erreurs liées au mode non-strict (voir la section « Erreurs à l'exécution » ci-avant)
- Minimisez l'utilisation des éléments dont la sémantique pourrait changer :
eval
: n'utilisez cette fonction uniquement si vous êtes certains que c'est l'unique solutionarguments
: utilisez les arguments d'une fonction via leur nom ou faites une copie de l'objet en utilisant :
var args = Array.prototype.slice.call(arguments)
au tout début de votre fonctionthis
: n'utilisezthis
uniquement pour faire référence à un objet que vous avez créé