55 votos

Indirectos eval llamada en modo estricto

Entiendo acerca de cómo eval() trabaja en la no-estricto contextos, sin embargo, el caso de uso eval() en modo estricto completamente confundido mí. Cuando eval() es llamado directamente en el ámbito global, las variables se mantienen dentro de la nueva eval() alcance:

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

Sin embargo, si realizo una indirecta llamada a eval() en el ámbito global (debe ser la misma cosa, ¿no?), actúa como si no es en modo estricto (si no me cree, vea este JSFiddle):

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

Si usted no entiende lo (0, eval) hace, vea ¿por Qué el uso de la página principal de google (0, obj.func)(args) sintaxis?.

Al menos según mi comprensión de lo eval() se supone que funciona en modo estricto, es decir, (no importa si eval() se denomina directa o indirectamente) de crear un nuevo ámbito de las variables definidas en la eval() llamada, sin embargo este no parece ser el caso aquí. La especificación dice lo siguiente:

10.4.2 Entrar Eval Código

Los siguientes pasos se realizan cuando el control entra en el contexto de ejecución de eval código:

  1. Si no hay ningún contexto de llamada o si la eval código no está siendo evaluado por una llamada directa (15.1.2.1.1) a la función eval entonces,

    una. Inicializar el contexto de ejecución, como si de un mundial en el contexto de ejecución utilizando el código eval como C , como se describe en 10.4.1.1.

  2. Otra cosa,

    una. Establecer el ThisBinding para el mismo valor de la ThisBinding de la vocación del contexto de ejecución.
    b. Establecer el LexicalEnvironment para el mismo valor de la LexicalEnvironment de la vocación del contexto de ejecución.
    c. Establecer el VariableEnvironment para el mismo valor de la VariableEnvironment de la vocación del contexto de ejecución.

  3. Si la eval código estricto código, a continuación,

    una. Vamos a strictVarEnv ser el resultado de llamar a NewDeclarativeEnvironment pasar el LexicalEnvironment como argumento.
    b. Establecer el LexicalEnvironment a strictVarEnv.
    c. Establecer el VariableEnvironment a strictVarEnv.

  4. Realizar la Declaración de Unión de la creación de instancias como se describe en 10.5 utilizando el código eval.

Este es el caso en todos los principales navegadores, incluyendo (pero no limitados a) de Internet Explorer 10, Chrome 30 y Firefox 24 -, ya que todos ellos tienen el mismo comportamiento, no creo que lo más probable es que es un bug. No se que tanto significaba para hacer la misma cosa, y si no, ¿por qué es este el caso?

Nota: por favor, no me digas que no uso eval() (sí, sé que los "peligros" de uso eval()) - simplemente quiero entender la lógica detrás de esto, que es absolutamente confuso para mí.

37voto

Benjamin Gruenbaum Puntos 51406

tl;dr

El segundo (0, eval)('var a = 1;'); de casos es de hecho una llamada directa.

Usted puede ver esto más frecuentemente en:

(function(){ "use strict"
    var x = eval;
    x("var y = 10"); // look at me all indirect
    window.y;// 10
    eval("var y = 11");
    window.y;// still 10, direct call in strict mode gets a new context
})();

El problema puede ser visto en:

Si la eval código estricto código, entonces (me: revisión del contexto)

Pero las estrictas eval código se define como:

Eval código estricto eval código, si empieza con una Directiva Prólogo que contiene un Uso Estricto de la Directiva o si la llamada a eval es una llamada directa.

Desde que la llamada no es directa, la eval código no es estricta eval código y la ejecución es de alcance global.


Primero de todo gran pregunta.

"Eval Código" es más general que la directa o indirecta llamada a eval.

Vamos a comprobar la especificación exacta de la eval función

15.1.2.1 eval (x)

Cuando la función eval se llama con un argumento x, se realizan los siguientes pasos:

  1. Si es del Tipo(x) no es Cadena, volver x.

  2. Vamos a prog ser el ECMAScript código que es el resultado del análisis de x como un Programa. Si el analizar falla, lanzar una SyntaxError excepción (pero véase también la cláusula 16).

  3. Vamos a evalCtx ser el resultado de establecer un nuevo contexto de ejecución (10.4.2) para la eval código prog.

  4. Vamos a resultado ser el resultado de la evaluación del programa prog.

  5. Salir de la ejecución del contexto de ejecución evalCtx, restaurar el contexto de ejecución anterior. ...

Por lo tanto, vamos a explorar lo que 10.4.2 nos dice, usted citó que - en específico echemos un vistazo a la primera cláusula:

Si no hay ningún contexto de llamada o si la eval código no está siendo evaluado por una llamada directa (15.1.2.1.1) a la función eval, a continuación, ... Inicializar el contexto de ejecución, como si de un mundial en el contexto de ejecución

Entonces, ¿qué es una llamada directa?

Una llamada directa a la función eval es la que se expresa como un CallExpression que cumpla las dos condiciones siguientes:

La Referencia que es el resultado de la evaluación de la MemberExpression en el CallExpression tiene un entorno registro como su valor y su nombre de referencia es "eval".

El resultado de llamar el resumen de la operación GetValue con esta Referencia como el argumento es el estándar incorporado en función definida en 15.1.2.1.

Así que, ¿cuál es el MemberExpression en ambos casos?

En eval('var a = 1;'); , de hecho, el resultado de la evaluación tiene un nombre de referencia eval y llamando GetValue resolución sobre devuelve la función integrada.

En (0, eval)('var a = 1;'); el resultado de evaluar la expresión de miembro ¿ no tienes un nombre de referencia eval. (Resuelve el construido en función de GetValue).

¿Qué son los nombres de referencia de todos modos?

Sección 8.7 en la especificación nos dice:

Una Referencia es un nombre resuelto de unión. Una Referencia consta de tres componentes, el valor de la base, el nombre de referencia y el Booleano valorado estricto de referencia de la bandera. La base de valor es undefined, un Objeto, un Booleano, una Cadena, un Número, o un ambiente de registro (10.2.1). Un valor de la base de indefinido indica que la referencia no puede ser resuelto en un enlace. El nombre de referencia es una Cadena.

Esto nos obliga a mirar en la GetReferencedName:

GetReferencedName(V). Devuelve el nombre de referencia de los componentes de la referencia V.

Así, mientras que la expresión (0,eval) === eval es verdad, cuando la evaluación de la función, esto es en realidad una llamada indirecta a causa de nomenclatura.

Puedo ofrecer la Function constructor en su lugar :)?

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X