【问题标题】:How come eval doesn't have access to the scoped variables under a with statement?为什么 eval 不能访问 with 语句下的作用域变量?
【发布时间】:2010-08-07 19:06:51
【问题描述】:

为什么您不能在 with 语句下使用 eval 访问作用域变量?

例如:

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

编辑:正如知识渊博的 CMS 所指出的,这似乎是一个浏览器错误(使用 WebKit 控制台的浏览器)。

如果有人想知道我试图想出什么可憎的东西,那将需要“邪恶”evalwith——我想看看我是否可以获得一个函数(用作回调)在另一个上下文中执行,而不是在它定义的那个上下文中执行。不,我可能(咳)不会在任何地方使用它......比什么都好奇。

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

【问题讨论】:

  • 您在哪个浏览器中出现这种行为?您是否在某个控制台上运行代码?
  • @CMS:Chrome 5.0.375.125 beta 使用内置的开发者控制台。编辑:我刚刚用 Firefox (firebug) 尝试了这个,它按预期工作。一定是你说的浏览器错误。
  • @Daniel - 如果有帮助,它可以在 Chrome 6.0.472.22 中正常工作
  • @Nick,@Daniel,问题仅出现在控制台中我在 Chrome 6.0.472.25 上得到相同的行为,我很确定这个问题与 WebKit 控制台有关,因为它也可以在 Safari 5.0.1 和 WebKit Nightly 构建中重现
  • 似乎也不限于with 语句。简单的情况:(function (a) { eval("console.log(a)"); })("hello") 也失败了。我想我会提交问题,除非其他人愿意。

标签: javascript eval with-statement


【解决方案1】:

这是一个只能从 WebKit 控制台重现的错误,当从 FunctionExpression 调用 eval 时,它会出现绑定调用者上下文的问题。

当直接调用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 应该在调用者的词法环境中声明,而不是在全局上下文中。

如果我们尝试,FunctionDeclarations 的行为是完全正常的:

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 上运行的以下浏览器上重现该问题:

  • Chrome 5.0.375.125
  • Chrome 6.0.472.25 开发版
  • Safari 5.0.1
  • WebKit Nightly Build r64893

【讨论】:

    【解决方案2】:
    (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 中。

    从各种控制台运行 sn-ps 代码的问题在于,控制台通常会与上下文发生冲突。 (即 Chrome 控制台似乎没有在全局上下文中正确地包装东西,而 Firebug 控制台可以)。这可能是一个错误,或者(更有可能)它可以按预期工作。

    【讨论】:

      【解决方案3】:

      Eval 总是在全局范围内运行,不是吗?

      【讨论】:

      • 不,直接调用 eval 将使用调用上下文(调用者词法和变量环境),间接调用 ECMAScript 5 中的 eval,例如:var foo = eval; foo('code'); 将使用全局上下文,以及 Function 构造函数。
      【解决方案4】:

      撇开 eval 不谈,新的 bowsers 包括 ecma5 Function.prototype.bind 方法,用于在某些选定对象的范围内调用函数。

      对于较旧的浏览器,您可以伪造它-

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

      【讨论】:

      • 请注意,fallback 函数 不符合标准,它不能预填充或 curry 具有已知参数的函数。 This implementation 是最接近 ES5 规范的,在 ES3 引擎上运行。另外 binding 一个函数不会允许访问调用者的变量或词法环境(这似乎是 OP 最后想要的),它只能确保 this 值(和 curried 参数)将被持久化。
      猜你喜欢
      • 2020-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-24
      • 1970-01-01
      相关资源
      最近更新 更多