Introduit dans JavaScript 1.8.5
Le mode strict de ECMAScript 5 est une façon d'adhérer à une variante restrictive de JavaScript. Le mode strict n'est pas seulement un sous-ensemble de JavaScript : il a intentionellement des sémantiques différentes du code normal. Les navigateurs ne supportant pas le mode strict exécuteront le code d'une façon légèrement différente de ceux le supportant, donc il ne faut pas compter sur le mode strict pour s'éviter des tests sur les navigateurs ne le supportant pas. Les codes en mode strict et en mode non-strict peuvent coexister, ce qui fait que vous pouvez réécrire les scripts en mode strict de façon incrémentielle.
Le mode strict apporte quelques changements à la sémantique du JavaScript normal. Premièrement, le mode strict élimine quelques erreurs silencieuses de JavaScript en les changeant en erreurs rapportées. Deuxièmement, le mode strict corrige les erreurs qui font qu'il est difficile pour les moteurs JavaScript d'effectuer des optimisations: Votre code sera exécuté plus rapidement en mode strict, sans en changer une seule ligne. Troisièmement, le mode strict interdit les mot-clés susceptibles d'être définis dans les futures versions de ECMAScript.
Invoquer le mode strict
Le mode strict s'applique à des scripts entiers ou à des fonctions individuelles. Il ne peut s'appliquer à des blocs d'instructions entourés d'accolades {}; essayer de l'appliquer dans ces contextes ne fera rien. Les eval, Function, attributs d'événements et chaînes passées à setTimeout, ou autres sont des scripts entiers, et invoquer le mode strict à l'intérieur de ceux-ci fonctionnera comme prévu.
Le mode strict pour les scripts
Pour invoquer le mode strict pour un script entier, ajoutez l'instruction exacte "use strict"; (ou 'use strict';) avant toutes les autres instructions.
// Script entier en mode strict "use strict"; var v = "Allo! Je suis en mode strict!";
Cette syntaxe est un piège dans lequel est déjà tombé un site web majeur : il n'est pas possible de concaténer du script en mode strict ici et du code en mode non-strict. Vous ne pouvez plus par la suite ajouter du code en mode non-strict, à la suite de ce code ! L'inverse est aussi vrai : non-strict plus strict devient du non-strict. La concaténation de scripts en mode strict entre eux fonctionne bien, tout comme la concaténation des scripts en mode non-strict. Seule la concaténation de scripts stricts et non-stricts est problématique. Il est donc recommandé que vous activez le mode strict fonction par fonction (au moins pendant la période de transition).
Vous pouvez aussi adopter l'approche qui consiste à englober le code de votre script dans une fonction et donner à cette dernière le mode strict. Ce procédé élimine le problème de concaténation mais il signifie aussi que vous devez exporter chaque variable globale hors de la fonction principale nouvellement créée.
Le mode strict pour les fonctions
De même, pour activer le mode strict pour une fonction, mettre l'énoncé exact "use strict"; (ou 'use strict';) dans le corps de la fonction avant toute autre déclaration.
function strict(){
// Syntaxe en mode strict au niveau de la fonction
'use strict';
function nested() { return "Ho que oui, je le suis!"; }
return "Allô! Je suis une fonction en mode strict! " + nested();
}
function notStrict() { return "Je ne suis pas strict."; }
Changements dans le mode strict
Le mode strict modifie à la fois la syntaxe et le comportement à l'exécution. Les changements se déclinent généralement en trois catégories : ceux qui convertissent les fautes en erreurs (comme des erreurs de syntaxe ou les erreurs d'exécution), ceux qui simplifient comment une variable pour un nom donné est traité, simplifiant eval et arguments, et ceux qui rendent plus facile d'écrire du JavaScript "sécure", et anticipant les évolutions futures d'ECMAScript.
Convertir les fautes en erreurs
Le mode strict change quelques fautes précédemment acceptées, en erreurs. JavaScript a été conçu d'abord pour les développeurs novices et, quelquefois, il donne aux instructions qui devraient être des erreurs une sémantique sans erreur. Parfois cela règle un problème immédiatement, mais parfois aussi, ça peut créer d'autres erreurs, plus loin dans le code. Le mode strict traite ces fautes comme des erreurs afin qu'elles soient découvertes et rapidement réparées.
Premièrement, en mode strict, il est impossible de créer accidentellement des variables globales. En mode normal, mal écrire une variable dans une assignation crée une nouvelle propriété sur l'objet global et continue de "fonctionner" (même si ça peut être source de problèmes par la suite). Les assignations qui pourraient accidentellement créer des variables globales lèveront une erreur en mode strict:
"use strict"; varaibleMalTapee = 17; // lève un ReferenceError
Deuxièmement, le mode strict fait en sorte que les assignations qui échoueraient silencieusement lèveront aussi une exception. Par exemple, NaN est une variable globale en lecture seule. En mode normal, une assignation à NaN ne fera rien; le développeur ne recevra aucun retour par rapport à cette faute. En mode strict, assigner NaN lèvera une exception. Toute assignation qui échouera silencieusement en mode non-strict (assignation à une propriété en lecture seule, assignation à une propriété sans méthode "set", assignation à une nouvelle propriété sur un objet non-extensible) lèvera une exception en mode strict :
"use strict";
// Assignation à une propriété en lecture seule
var obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // lève un TypeError
// Assignation à une propriété qui n'a qu'une méthode get
var obj2 = { get x() { return 17; } };
obj2.x = 5; // lève un TypeError
// Assignation d'une nouvelle propriété à un objet non-extensible
var gele = {};
Object.preventExtensions(fixed);
gele.nouvelleProp = "ohé"; // lève un TypeError
Troisièmement, le mode strict fera que le fait d'essayer de supprimer des propriétés non-supprimables lèvera une exception (là où avant cela ne produisait aucun effet):
"use strict"; delete Object.prototype; // lève un TypeError
Quatrièmement, le mode strict requiert que toutes les propriétés nommées dans un objet littéral soit unique. En mode non-strict, les propriétés peuvent être spécifiées deux fois, JavaScript ne retenant que la dernière valeur de la propriété. Cette duplication en devient alors une source de confusion, surtout dans le cas où dans une modification de ce même code on se met à changer la valeur de la propriété autrement qu'en changeant la dernière instance. Les noms de propriété en double sont une erreur de syntaxe en mode strict :
"use strict";
var o = { p: 1, p: 2 }; // !!! erreur de syntaxe
Cinquièmement, le mode strict requiert que les noms de paramètres de fonction soient uniques. En mode non-strict le dernier argument dupliqué cache les arguments précédents de même nom. Ces arguments précédents demeurent disponibles via arguments[i], ils ne sont donc pas complètement inaccessibles. Pourtant, cette cachette n'a guère de sens et n'est probablement pas souhaitable (cela pourrait cacher une faute de frappe, par exemple). Donc en mode strict, les doublons de noms d'arguments sont une erreur de syntaxe:
function somme(a, a, c){ // !!! erreur de syntaxe
"use strict";
return a + b + c; // Ce code va planter s'il est exécuté
}
Sixièmement, le mode strict interdit la syntaxe octale. La syntaxe octale ne fait pas partie d'ECMAScript, mais elle est supportée dans tous les navigateurs en préfixant le nombre octal d'un zéro : 0644 === 420 et "\045" === "%". Les développeurs novices croient parfois qu'un zéro débutant un nombre n'a pas de signification sémantique, alors ils l'utilisent comme moyen d'aligner des colonnes de nombres — mais ce faisant, ils changent la valeur du nombre ! La syntaxe octale est rarement utile et peut être utilisée de manière fautive, donc le mode strict le considère comme étant une erreur de syntaxe :
"use strict";
var somme = 015 + // !!! erreur de syntaxe
197 +
142;
Simplifier l'utilisation des variables
Le mode strict simplifie la façon dont les noms de variables sont mis en correspondance aux définitions de variables dans le code. De nombreuses optimisations du compilateur reposent sur la capacité à dire à quel endroit la variable X est stockée : cela est essentiel pour optimiser pleinement le code JavaScript. JavaScript rend parfois cette mise en correspondance du nom de définition de la variable dans le code impossible à réaliser avant l'exécution du code. Le mode strict élimine la plupart des cas où cela se produit, de sorte que le compilateur peut mieux optimiser le code en mode strict.
Premièrement, le mode strict interdit l'utilisation de with. Le problème avec with est que tout nom de variable à l'intérieur du bloc peut référer à une propriété de l'objet qui lui est passé, ou encore à une variable déclarée à l'extérieur du bloc, globale ou non, à l'exécution : il est impossible de le savoir d'avance. Le mode strict fait de with une erreur de syntaxe, donc il n'y a aucune chance pour qu'un nom déclaré dans un with fasse référence à un lieu inconnu à l'exécution:
"use strict";
var x = 17;
with (obj) // !!! erreur de syntaxe
{
// Si on n'était pas en mode strict, serait-ce var x,
// ou serait-ce plutôt obj.x? Il est impossible en général
// de le dire sans faire tourner le code, donc
// le nom ne peut pas être optimisé.
x;
}
Au lieu d'utiliser with, on peut très bien assigner l'objet à une variable avec un nom court, puis accéder aux propriétés correspondantes à cette variable.
Deuxièmement, eval en mode strict ne doit pas créer de variables dont la portée dépasse celle du eval. En non-strict, eval("var x;") crée la variable x dans le code appelant eval. Ce qui signifie qu'en général, dans une fonction contenant un appel à eval, tous les noms qui ne réfèrent pas à un paramètre ou une variable locale devront être mis en correspondance à une définition de variable, à l'exécution (puisque cet eval a introduit une nouvelle variable qui serait susceptible de modifier la variable externe). En mode strict, eval crée des variables seulement pour le code étant évalué, donc eval ne peut pas réaliser d'affectation à une variable externe ou à une variable locale :
var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
assert(x === 17);
assert(evalX === 42);
De la même manière, si la fonction eval est invoquée par une expression de la forme eval(...) dans un code en mode strict, le code sera aussi évalué en mode strict. Le code peut déclarer explicitement le mode strict, mais il est inutile de le faire.
function strict1(str){
"use strict";
return eval(str); // str sera évalué en mode strict
}
function strict2(f, str){
"use strict";
return f(str); // pas de eval(...) : str est strict si et seulement si il est déclaré en mode strict
}
function nonstrict(str){
return eval(str); // str est strict si et seulement si il est déclaré en mode strict
}
strict1("'Mode strict!'");
strict1("'use strict'; 'Mode strict!'");
strict2(eval, "'Mode non-strict.'");
strict2(eval, "'use strict'; 'Mode strict!'");
nonstrict("'Mode non-strict.'");
nonstrict("'use strict'; 'Mode strict!'");
Ainsi, les noms dans le code eval en mode strict se comportent de la même façon que les noms dans le code en mode strict n'étant pas évalués comme le résultat de eval.
Troisièmement, le mode strict interdit la suppression des variables déclarées. delete name en mode strict est une erreur de syntaxe:
"use strict";
eval("var x; delete x;"); // !!! erreur de syntaxe
Rendre eval et arguments plus simples
Le mode strict rend arguments et eval moins bizarrement magiques. Les deux impliquent une quantité considérable de comportements magiques dans le code en mode non-strict : eval pour ajouter et enlever des liaisons et pour changer les valeurs de liaisons, et arguments par ses propriété indexées référant à des arguments nommés. Le mode strict fait de grands efforts afin de traîter eval et arguments comme des mots-clés, bien qu'une réparation complète ne devrait pas arriver avant une version future d'ECMAScript.
Premièrement, les chaînes eval et arguments ne peuvent pas être utilisées comme identificateur. Tous les exemples suivants sont des erreurs de syntaxe:
"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) { } };
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
var f = new Function("arguments", "'use strict'; return 17;");
Deuxièmement, en mode strict on ne donnera pas d'alias aux propriétés de arguments aux objets créées dans la fonction. En code normal, dans une fonction dont le premier argument est arg, affecter arg affectera aussi arguments[0], et vice versa (à moins qu'aucun argument ne soit fourni ou que arguments[0] soit supprimé). Les objets de arguments pour les fonctions en mode strict stockent les argument originaux, au moment où la fonction a été appelée. arguments[i] ne reflète pas la valeur de l'argument nommé correspondant, et vice-versa.
function f(a){
"use strict";
a = 42;
return [a, arguments[0]];
}
var pair = f(17);
assert(pair[0] === 42);
assert(pair[1] === 17);
Troisièmement, arguments.callee n'est plus supporté. En temps normal arguments.callee contient la référence de la fonction courante. Fonctionnalité inutile : vous n'avez qu'à nommer la fonction courante ! arguments.callee en mode strict est une propriété non supprimable qui lèvera une erreur si définie ou récupérée :
"use strict";
var f = function() { return arguments.callee; };
f(); // throws a TypeError
"Sécuriser" JavaScript
Le mode strict rend plus facile d'écrire du code Javascript "sécure". Quelques sites web fournissent maintenant les manières pour les utilisateurs d'écrire du javascript qui sera exécuté par le site pour d'autres utilisateurs. Le JavaScript, dans les navigateurs, peut accéder aux informations personnelles de l'utilisateur, de sorte que le JavaScript peut être partiellement transformé avant son exécution, pour supprimer l'accès à des fonctionnalités interdites. La flexibilité de JavaScript fait qu'il est à peu près impossible de le faire sans plusieurs vérifications à l'exécution. Certaines fonctions du langage sont si envahissantes que de faire les vérifications à l'exécution a un coût de performance considérable. Mettre le code en mode strict réduit de manière substantielle le besoin de faire ces vérifications à l'exécution.
Premièrement, la valeur passée en tant que this à une fonction en mode strict n'est pas enfermée dans un objet. Pour une fonction normale, this est toujours un objet : l'objet fourni si appelé avec un objet this ; la valeur, enfermée, si appelée avec un booléen, une chaîne de caractères, ou un nombre this ; ou l'objet global si appelé avec un this qui est undefined ou null. (Utilisez call, apply, ou bind pour spécifier un this particulier.) L'enfermement automatique est un coût en termes de performance, mais l'exposition de l'objet global dan les navigateurs est un problème de sécurité, parce que l'objet global fournit un accès à des fonctionalités "sécures" que les environnements javascript doivent restrindre. Ainsi, pour une fonction en mode strict, le this spécifié reste ce qu'il était. Mais, s'il n'est pas spécifié, this sera undefined:
"use strict";
function fun() { return this; }
assert(fun() === undefined);
assert(fun.call(2) === 2);
assert(fun.apply(null) === null);
assert(fun.call(undefined) === undefined);
assert(fun.bind(true)() === true);
Deuxièmement, en mode strict, il n'est plus possible de "parcourir" la pile JavaScript via les extensions communément implémentées à ECMAScript. En code normal avec ces extensions, quand une fonction fun est en train d'être appelée, fun.caller est la fonction qui a dû appeler le plus récemment fun, et fun.arguments sont les arguments de cette dernière invocation de fun. Ces deux extensions sont problématiques pour du JavaScript "sécure", parce qu'ils permettent à du code "sécurisé" d'accéder à des fonctions "privées" et leurs arguments (potentiellement non-sécures). Si fun est en mode strict, fun.caller et fun.arguments sont des propriétés non supprimables qui lèveront une erreur si elle sont invoquées, que ce soit en lecture ou en écriture :
function restricted()
{
"use strict";
restricted.caller; // lève une TypeError
restricted.arguments; // lève une TypeError
}
function privilegedInvoker()
{
return restricted();
}
privilegedInvoker();
Troisièmement, pour les fonctions en mode strict arguments ne fournit plus d'accès aux variables déclarées aux appels de fonctions correspondants. Dans quelques-unes des anciennes implémentations ECMAScript, arguments.caller était un objet dont les propriétés étaient des alias pour les variables de cette fonction. C'est une faille de sécurité parce que cela casse la possibilité de cacher des valeurs privées via l'abstraction de la fonction ; elle empêche également la plupart des optimisations. Pour toutes ces raisons, plus aucun navigateur récent ne l'implémente. Pourtant, en raison de sa fonctionnalité historique, arguments.caller pour une fonction en mode strict est aussi une propriété non supprimable qui lève une erreur quand elle est réfinie ou récupérée :
"use strict";
function fun(a, b)
{
"use strict";
var v = 12;
return arguments.caller; // lève une TypeError
}
fun(1, 2); // v ne sera pas exposé (ou a ou b)
Paver la voie pour les versions futures d'ECMAScript
Les version futures d'ECMAScript introduiront probablement la nouvelle syntaxe, et le mode strict en ECMAScript 5 appliquera quelques restrictions pour faciliter la transition. Il sera plus facile de faire des changements si les bases de ces changements sont interdites en mode strict.
Premièrement, en mode strict, une courte liste d'identifiants deviennent des mots réservés. Ces mots sont implements, interface, let, package, private, protected, public, static, et yield. En mode strict, donc, vous ne pouvez pas nommer ou utiliser des variables ou des arguments avec ces termes.
function package(protected){ // !!!
"use strict";
var implements; // !!!
interface: // !!!
while (true){
break interface; // !!!
}
function private() { } // !!!
}
function fun(static) { 'use strict'; } // !!!
Deux mises en gardes spécifiques à Mozilla : D'abord, si votre code est en JavaScript 1.7 ou plus (Dans du XUL, ou encore vous utilisez le bon <script type="">) et est en mode strict, let et yield ont la fonctionnalité qu'ils avaient depuis que ces mots-clés ont été introduits. Mais le code en mode strict sur le web, chargés avec <script src=""> ou <script>...</script>, ne sera pas capable d'utiliser let/yield comme identifiants. Deuxièmement, puisque ES5 réserve sans conditions les mots class, enum, export, extends, import, et super, d'avant Firefox 5, Mozilla les réserve donc seulement en mode strict.
Ensuite, le mode strict interdit les déclarations de fonction qui ne sont pas au plus haut niveau d'un script ou une fonction. En code normal dans les navigateurs, les déclarations de fonctions sont permises "partout". Cela ne fait pas partie de ES5 (ou même ES3) ! C'est une extension avec des sémantiques incompatibles dans les différents navigateurs. Les futures éditions d'ECMAScript spécifieront heureusement de nouvelles sémantiques pour les déclarations de fonctions qui ne sont pas au plus haut niveau du script ou de la fonction. Interdire ce genre de déclaration en mode strict "prépare le terrain" pour une spécification dans une version future d'ECMAScript :
"use strict";
if (true){
function f() { } // !!! erreur de syntaxe
f();
}
for (var i = 0; i < 5; i++){
function f2() { } // !!! erreur de syntaxe
f2();
}
function baz(){ // ok
function eit() { } // aussi ok
}
Cet interdit n'est pas propre au mode strict, parce que ces déclarations de fonctions sont une extension du ES5 de base. Mais c'est une recommandation du comité ECMAScript, et les navigateurs l'implémenteront.
Le mode strict dans les navigateurs
Les navigateurs n'implémentent pas encore de manière fiable le mode strict, donc n'en dépendez pas aveuglément. Le mode strict change la sémantique. Se fier sur ces changements causera des erreurs dans les navigateurs qui ne l'ont pas implémenté. Faites attention en utilisant le mode strict, et faites plutôt confiance au mode strict avec des tests de fonctionnalités qui vérifient si les parties pertinentes de mode strict sont mises en œuvre. Enfin, assurez-vous de tester votre code dans les navigateurs qui supportent et qui ne supportent pas le mode strict. Si vous ne testez qu'avec ceux qui ne le supportent pas, vous pourriez avoir des problèmes avec ceux qui le supportent, et vice versa.
Voir aussi
- Where's Walden? » New ES5 strict mode support: now with poison pills!
- Where's Walden? » New ES5 strict mode requirement: function statements not at top level of a program or function are prohibited
- Where's Walden? » New ES5 strict mode support: new vars created by strict mode eval code are local to that code only
- John Resig - ECMAScript 5 Strict Mode, JSON, and More
- ECMA-262-5 in detail. Chapter 2. Strict Mode.
- Strict mode compatibility table