eval()

La fonction eval() permet d'évaluer du code JavaScript représenté sous forme d'une chaîne de caractères.

Attention : L'exécution de JavaScript à partir d'une chaîne de caractères constitue un risque de sécurité énorme. Il est beaucoup trop facile pour un mauvais acteur d'exécuter du code arbitraire lorsque vous utilisez eval(). Voir la section N'utilisez eval() qu'en dernier recours ! ci-dessous.

Exemple interactif

Syntaxe

js
eval(str);

Paramètres

str

Une chaîne de caractères qui représente une expression JavaScript ou une instruction ou une suite d'instructions JavaScript. L'expression utilisée peut contenir des variables et des propriétés d'objets existants.

Valeur de retour

La valeur de terminaison du code fourni en argument. Si la valeur de terminaison est vide, c'est la valeur undefined qui est renvoyée.

Description

eval() est une fonction rattachée à l'objet global.

eval() prend en compte un argument qui est une chaîne de caractères. Si cette chaîne représente une expression, eval() évaluera l'expression. Si l'argument utilisé représente une ou plusieurs instructions JavaScript, eval() évaluera les instructions. eval() ne doit pas être utilisé pour évaluer une expression arithmétique. En effet, JavaScript évalue automatiquement les expressions arithmétiques.

Si on construit une expression arithmétique sous la forme d'une chaîne de caractères, on peut utiliser eval() pour évaluer cette expression par la suite. Ainsi, si on a une variable x, on peut préparer une expression à utiliser plus tard en construisant la chaîne "3 * x + 2" par exemple. Au moment où on souhaite procéder à l'évaluation, on appellera eval() avec cette chaîne de caractères.

Si l'argument passé à eval() n'est pas une chaîne de caractères, eval() renverra l'argument inchangé. Dans l'exemple qui suit, on utilise le constructeur String, eval() renvoie donc un objet String au lieu d'évaluer la chaîne de caractères correspondante.

js
eval(new String("2 + 2")); // renvoie un objet String contenant "2 + 2"
eval("2 + 2"); // renvoie 4

Ce comportement peut être résolu de façon générique en utilisant la méthode toString().

js
var expression = new String("2 + 2");
eval(expression.toString());

Si la fonction eval est utilisée de manière indirecte, en l'invoquant par une référence autre que eval, cela fonctionnera avec une portée globale plutôt que locale (d'après ECMASCript 5). Par exemple, les déclarations de fonctions vont créer des fonctions globales et le code en cours d'évaluation n'aura pas accès aux variables locales déclarées avec la même portée que là où la fonction eval est appelée.

js
function test() {
  var x = 2,
    y = 4;
  console.log(eval("x + y")); // Appel direct, portée locale, résultat de 6
  var geval = eval;
  console.log(geval("x + y")); // Appel indirect, portée globale, lance une exception ReferenceError car `x` n'est pas défini
  (0, eval)("x + y"); // un autre exemple d'appel indirect.
}

N'utiliser eval() qu'en dernier recours !

eval() est une fonction dangereuse qui exécute le code passé en argument avec les privilèges de l'environnement appelant. Si eval() est utilisée avec une chaîne construite de façon mal intentionnée, cela pourra entraîner l'exécution d'un code malveillant sur la machine de l'utilisateur avec les permissions données au site ou au module complémentaire. À un niveau encore plus critique, du code tiers pourrait ainsi consulter la portée dans laquelle eval() a été invoquée. Cela peut permettre des attaques qui n'auraient pas été rendues possible en utilisant un objet Function.

eval() est également plus lente que les méthodes alternatives. En effet, l'évaluation nécessite de faire appel à l'interpréteur JavaScript alors que de nombreuses structures sont optimisées par les moteurs JavaScript modernes.

Dans de nombreux cas, il existe des alternatives plus sûres et plus performantes à eval().

De plus, les moteurs JavaScript modernes convertissent le code JavaScript en code machine. Les notions relatives aux noms des variables sont donc transformées. Utiliser eval() force le navigateur à enregistrer puis à rechercher parmi les noms existants afin de retrouver les variables. Si besoin, on peut utiliser le constructeur Function :

Avec eval() :

js
function looseJsonParse(obj) {
  return eval("(" + obj + ")");
}
console.log(looseJsonParse("{a:(4-1), b:function(){}, c:new Date()}"));

Avec Function :

js
function looseJsonParse(obj) {
  return Function('"use strict";return (' + obj + ")")();
}
console.log(looseJsonParse("{a:(4-1), b:function(){}, c:new Date()}"));

Dans le premier cas, l'évaluation de c: new Date() sera beaucoup plus lente car Date peut faire référence à une variable déclarée avant. Dans le second cas, la fonction est évaluée dans la portée globale et le moteur peut donc utiliser Date directement.

Autrement dit, dans le premier cas, on aurait pu avoir un code comme :

js
function Date(n) {
  return [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursaday",
    "Friday",
    "Saturday",
    "Sunday",
  ][n % 7 || 0];
}
function looseJsonParse(obj) {
  return eval("(" + obj + ")");
}
console.log(looseJsonParse("{a:(4-1), b:function(){}, c:new Date()}"));

Auquel cas, le navigateur doit effectuer une recherche coûteuse afin de vérifier s'il y a des variables locales Date.

Pour obtenir un résultat identique, on peut tout à fait se passer d'eval() :

js
function Date(n) {
  return [
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursaday",
    "Friday",
    "Saturday",
    "Sunday",
  ][n % 7 || 0];
}
function runCodeWithDateFunction(obj) {
  return Function('"use strict";return (' + obj + ")")()(Date);
}
console.log(runCodeWithDateFunction("function(Date){ return Date(5) }"));

1. Le code passé à runCodeWithDateFunction peut être minifié.

2. Le surcoût lié à un appel de fonction est léger

3. Function() permet d'utiliser "use strict"; (qui peut également aider à améliorer les performances).

Enfin, pour la plupart des cas, on doit pouvoir éviter de passer par

eval() ou Function() !

Accéder aux propriétés d'un objet

eval() ne doit pas être utilisée pour convertir des noms de propriétés en propriétés. Par exemple, lorsqu'on ne sait pas quelle propriété va être consultée avant l'exécution, on pourrait utiliser :

js
var obj = { a: 20, b: 30 };
var nomPropriété = getNomProp(); //une méthode qui renvoie "a" ou "b"

eval("var résultat = obj." + nomPropriété);

Cependant, eval() n'est pas du tout nécessaire. Il est beaucoup plus simple, plus sécurisé, plus rapide, d'utiliser les accesseurs de propriétés :

js
var obj = { a: 20, b: 30 };
var nomPropriété = getNomProp(); // une méthode qui renvoie  "a" or "b"
var résultat = obj[nomPropriété]; //  obj[ "a" ] correspond à obj.a

Utiliser des fonctions au lieu de morceaux de code

Les fonctions JavaScript sont des citoyens de premier rang du langage, cela signifie que les fonctions peuvent être passées comme arguments aux autres API, qu'elles peuvent être stockées dans des variables, dans des propriétés d'objets, etc. De nombreuses API pour le DOM fonctionnent en prenant en argument des fonctions :

js
// au lieu de setTimeout(" ... ", 1000) on utilisera :
setTimeout(function() { ... }, 1000);

// au lieu de elt.setAttribute("onclick", "...") on utilisera :
elt.addEventListener("click", function() { ... } , false);

Les fermetures (closures) sont utiles lorsqu'on souhaite obtenir des fonctions paramétrées sans avoir à concaténer des chaînes de caractères.

Convertir des chaînes JSON en objets JavaScript (parsing)

Si la chaîne utilisée avec eval() contient des données (par exemple, un tableau : "[1, 2, 3]") et non du code, il est conseillé d'utiliser du JSON, qui permet de représenter un sous-ensemble des données représentables en JavaScript.

On notera que la syntaxe JSON est limitée relativement à la syntaxe JavaScript. De nombreux littéraux JavaScript ne pourront être parsés en JSON (par exemple, les virgules à la fin des instructions ne seront pas autorisées et les noms de propriétés devront être compris entre simples quotes). Il est souvent préférable d'utiliser un outil de sérialisation JSON pour que les chaînes générées puissent être analysée en JSON.

Transmettre des données et non du code

Si on a par exemple une extension conçue pour parcourir le code d'une page web, on pourra transmettre des données XPath au lieu d'un code JavaScript.

Exécuter du code avec des privilèges restreints

S'il faut nécessairement exécuter du code, il faut le faire avec des privilèges restreints. Cela s'applique généralement aux modules complémentaires ou aux applications XUL. Pour cela, on pourra utiliser Components.utils.evalInSandbox.

Exemples

Utiliser eval()

Dans le code suivant, les deux instructions passées à eval() sous la forme d'une chaîne renvoient 42. La première évaluation porte sur la chaîne "x + y + 1" ; la seconde évaluation porte sur la chaîne de caractères "42".

js
var x = 2;
var y = 39;
var z = "42";
eval("x + y + 1"); // renvoie 42
eval(z); // renvoie 42

Utiliser eval() pour une chaîne d'instructions

Dans l'exemple qui suit, eval() est utilisée pour évaluer la chaîne de caractères str. Cette chaîne contient plusieurs instructions JavaScript qui affichent un message dans la console et qui affectent la valeur 42 à la variable z si x vaut cinq et 0 sinon. Lorsque la seconde instruction est exécutée, eval() entraînera l'exécution des instructions, les instructions seront évaluées et la valeur de z sera donc renvoyée.

js
var x = 5;
var str = "if (x == 5) {console.log('z vaut 42'); z = 42;} else z = 0; ";
console.log("z vaut " + eval(str));

Le résultat d'eval() est celui de la dernière expression

eval() renvoie la valeur de la dernière expression évaluée :

js
var str = "if ( a ) { 1+1; } else { 1+2; }";
var a = true;
var b = eval(str); // renvoie 2

console.log("b vaut : " + b);

a = false;
b = eval(str); // renvoie 3

console.log("b vaut : " + b);

eval() et les fonctions

Pour qu'une fonction soit restituée lors de l'évaluation, il est nécessaire d'encadrer l'expression contenue dans la chaîne de caractères avec des parenthèses :

js
var fctStr1 = "function a() {}";
var fctStr2 = "(function a() {})";
var fct1 = eval(fctStr1); // renvoie undefined
var fct2 = eval(fctStr2); // renvoie une function

Spécifications

Specification
ECMAScript Language Specification
# sec-eval-x

Compatibilité des navigateurs

BCD tables only load in the browser

Voir aussi