55 votes

Appel indirect à eval en mode strict

Je comprends comment fonctionne eval() dans les contextes non stricts, cependant le cas de l'utilisation de eval() en mode strict m'a totalement dérouté. Lorsque eval() est appelé directement dans la portée globale, les variables sont maintenues à l'intérieur de la nouvelle portée eval():

'use strict';
eval('var a = 1;');
console.log(a); // ReferenceError: a is not defined

Cependant, si j'effectue un appel indirect à eval() dans la portée globale (ça devrait être la même chose, non ?), cela agit comme s'il n'était pas en mode strict (si vous ne me croyez pas, consultez ce JSFiddle):

'use strict';
(0, eval)('var a = 1;'); // appel indirect à eval
console.log(a); // 1???

Pour une explication de ce que fait <code>(0, eval)</code> : voir <a href="https://stackoverflow.com/questions/19535601/why-does-google-main-page-use-0-obj-funcargs-syntax">Pourquoi la page principale de Google utilise-t-elle la syntaxe (0, obj.func)(args) ?</a>.

D'après ma compréhension de la façon dont eval() est censé fonctionner en mode strict, il est censé (peu importe si eval() est appelé directement ou indirectement) créer une nouvelle portée pour les variables définies dans l'appel eval(), cependant ce n'est pas le cas ici. (norme ECMA-262 5e éd 10.4.2)

C'est le cas dans tous les principaux navigateurs (y compris Internet Explorer 10, Chrome 30 et Firefox 24) donc je ne pense pas que ce soit un bug. Ne sont-ils pas censés faire la même chose, et si ce n'est pas le cas, pourquoi est-ce ainsi ?

Remarque : oui, je connais les "dangers" de l'utilisation de <code>eval()</code> - je veux simplement comprendre la logique derrière cela :)

3 votes

On dirait un bug pour moi. Comment as-tu exécuté le code ci-dessus? Quel navigateur/interpréteur JS?

0 votes

@AaronDigulla: dans le JSFiddle donné, Chrome 30.

0 votes

@AaronDigulla : Firefox 24 et IE10 présentent également ce comportement.

37voto

Benjamin Gruenbaum Points 51406

tl;dr

Le deuxième cas (0, eval)('var a = 1;'); n'est en fait pas un appel direct.

Vous pouvez voir cela plus fréquemment dans:

(function(){ "use strict"
    var x = eval;
    x("var y = 10"); // regardez-moi tout indirect
    window.y;// 10
    eval("var y = 11");
    window.y;// toujours 10, un appel direct en mode strict obtient un nouveau contexte
})();

Le problème peut être vu dans:

Si le code eval est du code strict, alors (moi: corriger le contexte)

Mais le code eval strict est défini comme suit:

Le code eval est du code eval strict s'il commence par une prologue de directive contenant une directive Use Strict ou si l'appel à eval est un appel direct.

Puisque l'appel n'est pas direct, le code eval n'est pas du code eval strict - et l'exécution se fait dans la portée globale.


Tout d'abord, excellente question.

"Code Eval" est plus général qu'un appel direct ou indirect à eval.

Vérifions la spécification exacte pour la fonction eval

15.1.2.1 eval (x)

Lorsque la fonction eval est appelée avec un argument x, les étapes suivantes sont suivies:

  1. Si le Type(x) n'est pas une chaîne, retourner x.

  2. Soit prog le code ECMAScript qui est le résultat de l'analyse de x en tant que Programme. Si l'analyse échoue, lancer une exception SyntaxError (mais voir aussi la clause 16).

  3. Soit evalCtx le résultat de l'établissement d'un nouveau contexte d'exécution (10.4.2) pour le code eval prog.

  4. Soit le résultat le résultat de l'évaluation du programme prog.

  5. Sortir du contexte d'exécution en cours evalCtx, en restaurant le contexte d'exécution précédent. ...

Explorons donc ce que 10.4.2 nous dit, vous avez cité cela - en particulier, regardons la première clause:

S'il n'y a pas de contexte d'appel ou si le code eval n'est pas évalué par un appel direct (15.1.2.1.1) à la fonction eval, alors ... Initialiser le contexte d'exécution comme s'il s'agissait d'un contexte d'exécution global

Qu'est-ce qu'un appel direct alors ?

Un appel direct à la fonction eval est un appel qui est exprimé comme une Expression d'Appel qui remplit les deux conditions suivantes:

La Référence qui est le résultat de l'évaluation de l'ExpressionMembre dans l'ExpressionAppel a un enregistrement d'environnement comme valeur de base et son nom de référence est "eval".

Le résultat de l'appel de l'opération abstraite GetValue avec cette Référence comme argument est la fonction intégrée standard définie en 15.1.2.1.

Alors, quelle est l'ExpressionMembre dans les deux cas ?

Dans eval('var a = 1;'); en effet, le résultat de son évaluation a un nom de référence eval et l'appel de résolution GetValue sur celui-ci renvoie la fonction incorporée.

Dans (0, eval)('var a = 1;'); le résultat de l'évaluation de l'expression membre n'a pas de nom de référence eval. (Il résout tout de même à la fonction intégrée sur GetValue).

Quels sont les noms de référence de toute façon ?

La section 8.7 dans la spécification nous dit:

Une Référence est un nom de liaison résolu. Une Référence se compose de trois éléments, la valeur de base, le nom référencé et le drapeau de référence stricte valorisé en booléen. La valeur de base est soit undefined, un Object, un Boolean, une String, un Number, ou un enregistrement d'environnement (10.2.1). Une valeur de base undefined indique que la référence n'a pas pu être résolue à une liaison. Le nom référencé est une chaîne.

Cela nous oblige à examiner le GetReferencedName:

GetReferencedName(V). Renvoie le composant de nom de référence de la référence V.

Ainsi, alors que l'expression (0,eval) === eval est vraie, lors de l'évaluation de la fonction, il s'agit en fait d'un appel indirect en raison du nommage.

Puis-je proposer le constructeur Function à la place :)?

0 votes

D'accord, pas mal. Cependant, cela n'explique pas l'étape 3 - que si elle est en mode strict, créez un nouveau contexte pour l'appel à eval.

1 votes

@Qantas94Heavy Je dois admettre que "If direct is true and the code that made the direct call to eval is strict code, then let strictCaller be true. Otherwise, let strictCaller be false." d'EcmaScript 6 est beaucoup plus clair. ES5 vous oblige à faire un effort supplémentaire. Le code en mode strict est défini comme suit : "Le code Eval est un code eval strict s'il commence par une Prologue Directive contenant une Directive Use Strict ou si l'appel à eval est un appel direct" - l'appel est un appel direct donc en réalité - la condition à l'étape 3 est fausse.

0 votes

Je voulais dire que l'appel n'est pas un appel direct.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X