(1, eval) ('esto') vs eval ('esto') en JavaScript?

85

Empiezo a leer patrones de JavaScript , algunos códigos me confunden.

var global = (function () {
    return this || (1, eval)('this');
}());

Aquí están mis preguntas:

Q1:

(1, eval) === eval?

¿Por qué y cómo funciona?

P2: ¿Por qué no solo

var global = (function () {
    return this || eval('this');
}());

o

 var global = (function () {
    return this;
}());
Shawjia
fuente
He actualizado el título porque este es un caso específico. Además, los paréntesis para el tipo específico de corchetes : [] y {} son completamente diferentes :)

Respuestas:

104

La diferencia entre (1,eval)y simple y viejo evales que el primero es un valor y el segundo es un valor. Sería más obvio si fuera algún otro identificador:

var x;
x = 1;
(1, x) = 1; //  syntax error, of course!

Esa es (1,eval)una expresión que cede eval(como diría (true && eval)o (0 ? 0 : eval)haría), pero no es una referencia a eval.

¿Por qué te importa?

Bueno, la especificación ECMA considera una referencia a evalser una "llamada directa eval", sino una expresión que se limita a rinde evala ser uno indirecto - y llamadas eval indirectos están garantizados para ejecutar en el ámbito global.

Cosas que todavía no sé:

  1. ¿En qué circunstancias no se ejecuta una llamada de evaluación directa en el ámbito global?
  2. ¿Bajo qué circunstancia puede el thisde una función en el alcance global no producir el objeto global?

Puede obtener más información aquí .

EDITAR

Aparentemente, la respuesta a mi primera pregunta es "casi siempre". Un directo se evalejecuta desde el alcance actual . Considere el siguiente código:

var x = 'outer';
(function() {
  var x = 'inner';
  eval('console.log("direct call: " + x)'); 
  (1,eval)('console.log("indirect call: " + x)'); 
})();

Como era de esperar (jejeje), esto imprime:

direct call: inner
indirect call: outer

EDITAR

Después de más experimentación, voy a decir provisionalmente que thisno se puede establecer en nullo undefined. Se puede establecer en otros valores falsos (0, '', NaN, falso), pero solo de forma muy deliberada.

Voy a decir que su fuente está sufriendo de una inversión cráneo-rectal leve y reversible y quizás quiera considerar pasar una semana programando en Haskell.

Malvolio
fuente
3
Vaya, no sabía todo el asunto del valuevs lvalue(bueno, en la práctica quizás, pero no en palabras). Tampoco las reglas de evaluación de ES5 (no que razonablemente deba usar evalalguna vez). ¡Gracias!
Stoive
Sí, evaltiene muchos bordes afilados desagradables y solo debe usarse como último recurso y luego, con mucho, mucho cuidado.
Malvolio
Solo una vez encontré un uso válido: evaluar una etiqueta de script que se había agregado al DOM a través deinnerHtml
Stoive
1
lvalue tiene poco que ver con la determinación de la evaluación directa, ya que generalmente se refiere a la expresión que puede aparecer en el lado izquierdo de una asignación, de ahí el nombre lvalue en lugar de rvalue. Una llamada a eval es directa solo bajo las condiciones enumeradas en 15.1.2.1.1 de la especificación que dice que el identificador debe ser evaly ser la parte MemberExpression de una CallExpression y hacer referencia a la evalfunción estándar .
chuckj
1
@Malvolio Parece que estás insinuando que los valores l tienen algo que ver con la evaluación directa o indirecta, lo cual no es así. El uso de un identificador llamado evalcomo destino de una expresión de llamada es especial. Afirmas que ECMA trata la referencia a evalespecial que no lo hace. Es la ubicación en la expresión de llamada lo que es especial y la expresión evalúa a la evalfunción estándar . Por ejemplo, var eval = window.eval; eval('1');sigue siendo una evaluación directa y window.eval('1')no lo es, aunque eval también es un valor l en este caso.
chuckj
33

El fragmento

var global = (function () {  
    return this || (1, eval)('this');  
}());  

evaluará correctamente el objeto global incluso en modo estricto. En el modo no estricto, el valor de thises el objeto global, pero en el modo estricto lo es undefined. La expresión (1, eval)('this')siempre será el objeto global. La razón de esto involucra las reglas en torno a los versos indirectos directos eval. Las llamadas directas a evaltienen el alcance de la persona que llama y la cadena thisse evaluaría con el valor de thisen el cierre. Los evalmensajes indirectos se evalúan en el ámbito global como si fueran ejecutados dentro de una función en el ámbito global. Dado que esa función no es en sí misma una función en modo estricto, el objeto global se pasa como thisy luego la expresión se 'this'evalúa como el objeto global. La expresión (1, eval)es solo una forma elegante de forzar laeval para ser indirecto y devolver el objeto global.

A1: (1, eval)('this')no es lo mismo eval('this')debido a las reglas especiales sobre llamadas directas a verso indirecto eval.

A2: El original funciona en modo estricto, las versiones modificadas no.

chuckj
fuente
12

A Q1:

Creo que este es un buen ejemplo de operador de coma en JS. Me gusta la explicación del operador de coma en este artículo: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/

El operador de coma evalúa ambos operandos (de izquierda a derecha) y devuelve el valor del segundo operando.

Al segundo trimestre:

(1, eval)('this')se considera una llamada de evaluación indirecta, que en ES5 ejecuta código globalmente. Entonces el resultado será el contexto global.

Ver http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope

Grace Shao
fuente
7

P1: Varias declaraciones de JavaScript consecutivas separadas por una coma toman el valor de la última declaración. Entonces:

(1, eval)toma el valor del último que es una referencia de función a la eval()función. Aparentemente, lo hace de esta manera para convertir la eval()llamada en una llamada de evaluación indirecta que se evaluará en el ámbito global en ES5. Detalles explicados aquí .

P2: Debe haber algún entorno que no defina un global this, pero sí defina eval('this'). Esa es la única razón en la que puedo pensar para eso.

jfriend00
fuente
¿Quizás alguien está tratando de esquivar un gancho de check-in que no permite /eval\(/g?
Stoive
@Stoive: sí, también me preguntaba sobre algo así. Si no es un gancho de registro, algunos filtran en algún lugar del proceso (quizás minimización).
jfriend00
7
Tiene algo que ver con el modo estricto de ES5. AFAIK en el modo estricto de ES5, cualquier evalcódigo 'd se ejecuta en su propio contexto en lugar de en el contexto global o el contexto adjunto. Una forma de evitar esto es hacer referencia a él indirectamente como lo hace el código en cuestión.
Cristian Sanchez
1
Eche un vistazo a " Evaluación global. ¿Cuáles son las opciones? ".
Saxoier
Actualicé mi respuesta para incluir la información de CDSanchez y @Saxoier. Gracias.
jfriend00