Nombreux sont les développeurs qui utilisent la fonction javascript eval qui permet d’évaluer du code javascript dans une chaine de caractères. Rares sont ceux qui savent que l’usage de cette fonction est une mauvaise pratique et est donc à proscrire. Je vous explique pourquoi.
En premier lieu, retenez l’adage “eval() is evil” [la fonction eval est démoniaque]. Il n’y a rien de plus parlant pour retenir que l’usage de la fonction javascript eval est à proscrire.
L’utilisation de eval() comporte des risques en termes de sécurité, le code exécutée de cette manière pourrait avoir été modifié.
C’est un antipattern commun surtout lorsqu’il s’agit d’une réponse JSON d’une requête Ajax. Dans ce cas, il conviendrait de valider la chaine de caractères (vérifier qu’il n’y a pas de code malveillant) avant toute exécution.
Vous pouvez utiliser les méthodes intégrées des navigateurs pour analyser la réponse JSON afin valider la chaine de caractères json avant tout usage avec la fonction eval(). Pour les navigateurs qui ne supportent pas JSON.parse() nativement, vous pouvez utiliser une bibliothèque de JSON.org.
Une meilleure pratique serait l’utilisation du new Function() similaire à eval() mais mieux sécurisée. L’avantage de cette méthode est le code évalué dans la nouvelle fonction Function() sera exécuté dans une fonction locale de sorte que les variables définies avec var dans le code en cours d’évaluation ne deviendront pas automatiquement des variables globales.
Une autre façon d’éviter les variables globales automatiques est d’encapsuler eval() dans une fonction anonyme autoexecutée.
Une petit exemple pour clarifier les choses, ou un seul cas vient polluer l’espace de noms global:
console.log(typeof un); // "undefined" console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined" var jsstring = "var un = 1; console.log(un);"; eval(jsstring); // affiche "1" dans la console js jsstring = "var deux = 2; console.log(deux);"; new Function(jsstring)(); // affiche "2" dans la console js jsstring = "var trois = 3; console.log(trois);"; (function () { eval(jsstring); }()); // affiche "3" dans la console js console.log(typeof un); // "number" -> défini via eval au grand jour console.log(typeof deux); // "undefined" -> défini via Function console.log(typeof trois); // "undefined" -> défini via eval dans fonction autoexécutée
Une autre différence entre eval() et le constructeur de fonction est qu’eval() peut perturber la chaîne du scope alors que Function est circonscrit au contexte de la fonction: tout ce qui y est défini n’est pas exposé ou visible de l’extérieur. Peu importe où vous exécutez la fonction.
Dans l’exemple suivant, eval() peut accéder et modifier une variable dans sa partie externe alors que la fonction ne peut pas (notez également que l’utilisation de Function ou new Function est identique) :
(function () { var local = 1; eval("local = 3; console.log(local)"); // logs 3 console.log(local); // logs 3 }()); (function () { var local = 1; Function("console.log(typeof local);")(); // logs undefined }());
Bonjour. Je ne comprends pas pourquoi d’entrée vous considérez que l’emploi d’eval() est à proscrire. Ce qui est à proscrire c’est de lui demander de traiter quelque chose qui n’est pas entièrement sous contrôle. Sinon c’est un mécanisme indispensable lorsque l’on veut exécuter du code que l’on vient de calculer par programme, ce qui explique qu’on trouve son équivalent dans beaucoup de langages interprétés (les ‘back quotes’ du shell, eval() sous PHP, etc.). D’ailleurs une qualité essentielle d’un interpréteur est de bénéficier d’une telle fonctionnalité, qu’un langage compilé n’offrira pas (à moins d’embarquer son propre compilateur…). Que certains s’en servent maladroitement est un autre problème…
Votre lecture est juste et il est agréable de converser avec des lecteurs avertis. Je comprends votre remarque “Ce qui est à proscrire c’est de lui demander de traiter quelque chose qui n’est pas entièrement sous contrôle” et je suis parfaitement en accord avec cela. Parce que concentré sur l’obtention d’un résultat nominal, manque de connaissance ou maladresse amènent souvent à des usages non sécurisés. Probablement une question de génération et aussi de sensibilité à la sécurité. Et donc proposer une solution moins contraignante et plus sécurisée a du sens en évitant l’apparition d’effets de bord inopportuns. Même s’il n’empêche au développeur d’avoir conscience et de maitriser quelques incontournables en sécurité.