2010-08-07 2 views
6

Почему не может получить доступ к переменным областям с использованием eval под оператором with?Как получилось, что eval не имеет доступа к переменным с областью действия под оператором with?

Например:

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

EDIT: Как хорошо осведомленный CMS отметил, это, кажется, браузер ошибка (браузеры, которые используют консоль WebKit).

Если кто-то задавался вопросом, какую мерзость я пытался придумать, это потребует как «зла» eval, так и with - Я пытался выяснить, могу ли я получить функцию (использованную как обратный вызов), выполненную в другой контекст, а не тот, в котором он был определен. И нет, я , вероятно, (кашель) не будет использовать это нигде .. более любопытно, чем что-либо.

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

В каком браузере вы получаете такое поведение? Вы используете код на какой-то консоли? – CMS

+0

@CMS: Chrome 5.0.375.125 beta, используя встроенную консоль разработчика. Изменить: я просто попробовал это с Firefox (firebug), и он работал, как ожидалось. Должна быть ошибка браузера, как вы сказали. –

+0

@ Daniel - он работает правильно в Chrome 6.0.472.22, если это помогает любому –

ответ

5

Это ошибка, воспроизводимая только с консоли WebKit, она имеет проблемы, связывающие контекст вызывающего абонента, когда eval вызывается из FunctionExpression.

Когда прямой вызов eval производится, оцененный код, как вы ожидаете, следует разделить как переменное окружение:

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

а также лексическую среду:

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

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

В приведенных выше функциях localVar должен быть объявлен в лексической среде вызывающего абонента, а не в глобальном контексте.

Для FunctionDeclaration S поведение совершенно нормально, если мы попытаемся:

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

И

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

Я был в состоянии воспроизвести проблему в следующих браузерах, работающих на Windows Vista SP2:

  • Хром 5.0.375.125
  • Chrome 6.0.472.25 dev
  • Safari 5.0.1
  • WebKit ночные сборки r64893
0

Eval всегда работает в глобальном масштабе, не так ли?

+0

Нет, прямой вызов 'eval' будет использовать вызывающий контекст (как лексическая, так и переменная окружения вызывающего), косвенный вызов eval в ECMAScript 5, например:« var foo = eval; foo ('code'); 'будет использовать глобальный контекст, а также конструктор Function. – 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") 

Все отлично работает: http://polyfx.com/jstest.html в FF/Chrome/Safari/IE.

Проблема с запуском фрагментов кода с различных консолей заключается в том, что консоль обычно винт с контекстом. (т. е. консоль Chrome не выглядит должным образом обертывать материал в глобальном контексте, тогда как консоль Firebug). Это может быть ошибка или (скорее всего) она может работать по назначению.

0

Ввод Eval и в сторону, новые баузеров включают метод ecma5 Function.prototype.bind, чтобы вызвать функцию в рамках какого-то выбранного объекта.

Для более старых браузеров, вы можете подделать it-

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

Обратите внимание, что * резервная функция * не соответствует стандартам, она не может предварительно заполнить или * карри * функцию с известными аргументами. [Эта реализация] (http://stackoverflow.com/questions/2025789/preserving-a-reference-to-this-in-javascript-prototype-functions/2025839#2025839) является наиболее близким к тому, что вы можете выполнить с ES5 спецификация, работающая на двигателе ES3. Также * привязка * функция не будет предоставлять доступ к переменной или лексической среде вызывающего (которая кажется, что OP хочет в конце), она может только сохранить, что аргументы 'this' (и * curried *) будут сохранены. – CMS