2012-02-02 8 views
68

Empiezo a leer JavaScript Patterns, algunos codes me confundieron.(1, eval) ('this') vs eval ('this') en JavaScript?

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

Aquí están mis preguntas:

P1:

(1, eval) === eval?

¿Por qué y cómo funciona?

P2: ¿Por qué no

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

o

var global = (function() { 
    return this; 
}()); 
+0

He actualizado el título porque este es un caso específico. Además, * paréntesis * para el tipo específico de * corchetes *: [] y {} son completamente diferentes :) –

Respuesta

81

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

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

Eso es (1,eval) es una expresión que produce eval (al igual que decir, (true && eval) o (0 ? 0 : eval) lo haría), pero no es una referencia a eval.

¿Por qué te importa?

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

cosas que todavía no sé:

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

Se puede obtener algo más de información here.

EDITAR

Al parecer, la respuesta a la primera pregunta es, "casi siempre". Un eval directo se ejecuta 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)'); 
})(); 
No es sorprendente que

(je, je), esto imprime:

direct call: inner 
indirect call: outer 

EDITAR

Después de más experimentación, voy a decir que provisionalmente this no se puede establecer en null o undefined. Se puede establecer en otros valores falsy (0, '', NaN, falso), pero solo muy deliberadamente.

Voy a decir que su fuente está sufriendo de una inversión craneo-rectal leve y reversible y que podría considerar pasar una semana de programación en Haskell.

+3

Guau, no sabía todo el 'valor' vs 'lvalue' cosa (bueno, en pra tal vez ctice, pero no en palabras). Tampoco las reglas de evaluación ES5 (no es que razonablemente necesite usar 'eval' alguna vez). ¡Gracias! – Stoive

+0

Sí, 'eval' tiene muchos bordes filosos desagradables y solo debe usarse como último recurso y luego, muy, muy cuidadosamente. – Malvolio

+0

Solo una vez encontré un uso válido, evaluando una etiqueta de script que se había agregado al DOM a través de 'innerHtml' – Stoive

7

P1: sentencias JavaScript consecutivos múltiples 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 función eval(). Aparentemente lo hace de esta manera para hacer que la llamada eval() se convierta en una llamada de evaluación indirecta que se evaluará en el alcance global en ES5. Detalles explicados here.

Q2: Debe haber algún entorno que no defina un this global, pero sí define eval('this'). Esa es la única razón por la que se me ocurre eso.

+0

Tal vez alguien está tratando de eludir un gancho de check-in que no permite '/ eval \ (/ g'? – Stoive

+0

@Stoive - Sí, me pregunté sobre algo así también. Si no es un checkin hook, algún filtro en algún lugar del proceso (tal vez minimización). – jfriend00

+7

Tiene algo que ver con el modo estricto ES5. AFAIK en modo estricto ES5 cualquier código 'eval' es ejecutado en su propio contexto en lugar de hacerlo en el contexto global o en el contexto circundante. Una forma de evitar esto es hacer referencia indirectamente al código en cuestión. –

11

Para P1:

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

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

Para Q2:

(1, eval)('this') se considera como llamada eval indirecta, que en ES5 hace ejecutar código a nivel mundial. Entonces, el resultado será el contexto global.

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

29

El fragmento,

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

evaluará correctamente al objeto global, incluso en modo estricto. En modo no estricto, el valor de this es el objeto global, pero en el modo estricto es undefined. La expresión (1, eval)('this') siempre será el objeto global. La razón de esto involucra las reglas alrededor de los versos indirectos directos eval. Las llamadas directas a eval tienen el alcance de la persona que llama y la cadena this evaluará el valor de this en el cierre. Los eval indirectos se evalúan en el ámbito global como si se hubieran ejecutado dentro de una función en el ámbito global. Como esa función no es en sí misma una función de modo estricto, el objeto global se pasa como this y luego la expresión 'this' se evalúa como el objeto global. La expresión (1, eval) es simplemente una forma elegante de forzar que el eval sea indirecto y devuelva el objeto global.

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

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