2010-08-07 9 views
6

¿Por qué no se puede acceder a variables de ámbito usando eval bajo una declaración with?¿Cómo es que eval no tiene acceso a las variables de ámbito bajo una declaración with?

Por ejemplo:

(function (obj) { 
    with (obj) { 
     console.log(a); // prints out obj.a 
     eval("console.log(a)"); // ReferenceError: a is not defined 
    } 
})({ a: "hello" }) 

EDITAR: A medida que el conocimiento CMS señaló, esto parece ser un error del navegador (los navegadores que utilizan la consola WebKit).

Si alguien se preguntaba qué abominación estaba tratando de encontrar que requeriría tanto el "mal" eval y with - Estaba tratando de ver si podía obtener una función (utilizada como devolución de llamada) ejecutada en otro contexto en lugar de la que se definió en. Y no, I probablemente (tos) no utilizará esto en cualquier lugar .. más curioso que nada.

(function (context,fn) { 
    with (context) 
     eval("("+fn+")()"); 
})({ a: "hello there" }, function() { console.log(a); }) 
+0

En qué navegador estás recibiendo este comportamiento? ¿Estás ejecutando el código en alguna consola? – CMS

+0

@CMS: Chrome 5.0.375.125 beta utilizando la consola de desarrollador integrada. Editar: Acabo de probar esto con Firefox (Firebug) y funcionó como se esperaba. Debe ser un error del navegador como dijiste. –

+0

@Daniel - Funciona correctamente en Chrome 6.0.472.22 si eso ayuda –

Respuesta

5

Se trata de un error reproducible solo desde la consola de WebKit, tiene problemas para vincular el contexto de la persona que llama cuando eval se invoca desde FunctionExpression.

Cuando se realiza una llamada directa de eval, el código evaluado como esperas deben compartir tanto la variable de entorno:

(function (arg) { 
    return eval('arg'); 
})('foo'); 
// should return 'foo', throws a ReferenceError from the WebKit console 

Y también el entorno léxico:

(function() { 
    eval('var localVar = "test"'); 
})(); 

typeof localVar; // should be 'undefined', returns 'string' on the Console 

En la función anterior localVar debe declararse en el entorno léxico de la persona que llama, no en el contexto global.

Para FunctionDeclaration s el comportamiento es completamente normal, si tratamos:

function test1(arg) { 
    return eval('arg'); 
} 
test1('foo'); // properly returns 'foo' on the WebKit console 

Y

function test2() { 
    eval('var localVarTest = "test"'); 
} 
test2(); 
typeof localVarTest; // correctly returns 'undefined' 

he podido reproducir el problema en los siguientes navegadores que se ejecutan en Windows Vista SP2:

  • Chrome 5.0.375.125
  • Chrome 6.0.472.25 dev
  • Safari 5.0.1
  • WebKit Nightly Build r64893
0

Eval siempre se ejecuta en alcance global, ¿no?

+0

No, una llamada directa a 'eval' usará el contexto de llamada (tanto el entorno léxico como variable de la persona que llama), una llamada indirecta a eval en ECMAScript 5, por ejemplo,' var foo = eval; foo ('code'); 'usará el contexto global, así como el constructor de la función. – CMS

1
(function (obj) { 
    with (obj) { 
     alert(a); // prints out obj.a 
     eval("alert(a)"); // ReferenceError: a is not defined 
    } 
})({ a: "hello from a with eval" }) 

function testfunc(a) { eval("alert(a)"); } testfunc("hello from a testfunc eval"); 

(function (a) { eval("alert(a)"); })("hello from a function constructor eval") 

Todos funcionan bien: http://polyfx.com/jstest.html en FF/Chrome/Safari/IE.

El problema con la ejecución de fragmentos de código de varias consolas es que las consolas generalmente se confunden con el contexto. (es decir, la consola de Chrome no parece estar envolviendo adecuadamente las cosas en el contexto global, mientras que la consola de Firebug sí). Podría ser un error o (más probable) podría estar funcionando como se esperaba.

0

Poner eval y con un lado, las nuevas Bowsers incluyen el método ecma5 Function.prototype.bind llamar a una función en el alcance de un objeto seleccionado.

Para los navegadores más antiguos que se puede fingir IT

Function.prototype.bind= Function.prototype.bind || function bind(scope){ 
    var method= this; 
    return function(){ 
     method.apply(scope, arguments); 
    } 
} 
+0

Tenga en cuenta que la * función de retorno * no cumple con los estándares, no puede rellenar previamente o * curry * una función con argumentos conocidos. [Esta implementación] (http://stackoverflow.com/questions/2025789/preserving-a-reference-to-this-in-javascript-prototype-functions/2025839#2025839) es lo más cercano que puede llegar a cumplir con el ES5 spec., ejecutándose en un motor ES3. También * binding * una función no dará acceso a la variable de la persona que llama o entorno léxico (que parece que el OP quiere al final), solo puede garantizar que el 'this' value (y * curried * arguments) se mantendrá. – CMS

Cuestiones relacionadas