Javascript global.eval无法访问词法范围中的变量。行为是否符合ECMAScript标准?

Javascript global.eval无法访问词法范围中的变量。行为是否符合ECMAScript标准?,javascript,dynamic,closures,eval,interpreter,Javascript,Dynamic,Closures,Eval,Interpreter,我有一个JavaScript文件,e.js var global = Function('return this')(); var i = 1; console.log(eval("100-1")); console.log(eval("i")); console.log(global.eval("100-1")); console.log(global.eval("i")); 当我通过V8执行它时: $ node e.js 99 1 99 undefined:1 i ^ Refere

我有一个JavaScript文件,
e.js

var global = Function('return this')();

var i = 1;

console.log(eval("100-1"));
console.log(eval("i"));

console.log(global.eval("100-1"));
console.log(global.eval("i"));
当我通过V8执行它时:

$ node e.js
99
1
99

undefined:1
i
^
ReferenceError: i is not defined
    at eval (eval at <anonymous> (/private/tmp/xxxx/e.js:8:20), <anonymous>:1:1)
    at eval (native)
    at Object.<anonymous> (/private/tmp/xxxx/e.js:8:20)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:902:3
$node e.js
99
1.
99
未定义:1
我
^
ReferenceError:未定义i
评估时(评估时(/private/tmp/xxxx/e.js:8:20),:1:1)
评估时(本地)
反对。(/private/tmp/xxxx/e.js:8:20)
在模块处编译(Module.js:456:26)
在Object.Module.\u extensions..js(Module.js:474:10)
在Module.load(Module.js:356:32)
在Function.Module.\u加载(Module.js:312:12)
位于Function.Module.runMain(Module.js:497:10)
启动时(node.js:119:16)
在node.js:902:3
因此,
global.eval
适用于数学运算符,但它无法访问变量
i
,而
eval
适用于这两种情况


这种行为是V8的限制吗?或者,根据ECMAScript标准,这是预期的行为吗?

是的,这是符合规范的行为,表示对
eval
的调用“直接”的一个要求是对
eval
的引用“有一个环境记录作为它的基值”。这意味着它不能是由属性访问完成的引用(在这种情况下,拥有的对象将是基值);它必须是一个“裸”函数

这种区别对于以下步骤1至关重要:

  • 如果没有调用上下文,或者eval代码没有通过对eval函数的直接调用(15.1.2.1.1)进行求值,则,
    • a。如10.4.1.1所述,使用评估代码C初始化执行上下文,如同它是全局执行上下文一样
  • 因此,对
    eval
    的间接调用提供了一个全局变量环境,而不是局部变量环境。只有直接呼叫才能访问本地环境

    这样做是出于实际实现的原因,因为
    eval
    可以向垃圾收集器发出避免清理任何变量的信号。例如,这里有一个没有
    eval
    的案例:

    function foo() {
        var a = 5, b = 6, c = 7;
        return function() { return a; }
    }
    var func = foo();
    alert(func());
    
    function foo() {
        var a = 5, b = 6, c = 7;
        return function(exp) { return eval(exp); }
    }
    var func = foo();
    alert(func("b"));
    
    foo
    返回的函数在
    foo
    终止后可能会访问
    a
    ,但我们可以确定
    b
    c
    foo
    终止后将永远不会再被访问
    b
    c
    可以安全地进行垃圾收集,而
    a
    保持未收集状态

    现在是一个带有
    eval
    的案例:

    function foo() {
        var a = 5, b = 6, c = 7;
        return function() { return a; }
    }
    var func = foo();
    alert(func());
    
    function foo() {
        var a = 5, b = 6, c = 7;
        return function(exp) { return eval(exp); }
    }
    var func = foo();
    alert(func("b"));
    
    通常无法确定
    eval
    表达式
    exp
    是否将引用给定的变量,因此垃圾收集器决不能收集任何变量,以便返回的函数仍然可以使用这些变量


    为了确定
    eval
    正在使用,解析器必须能够可靠地识别对
    eval
    的调用。如果
    eval
    是以一种间接的方式呈现的,比如
    global[“e”+“va”+“l!”[0]
    ,规范会说
    eval
    ed代码不能访问任何局部变量,从而避免垃圾收集问题。

    这是符合规范的行为;我会试着找一个复制品。请参阅和的步骤1,但为什么相同的代码在Google Chrome的JavaScript控制台上工作,而在Node.js上不工作?@user955091这是因为
    i
    在Chrome中不是一个局部范围的变量。节点可能将代码包装在
    (function(){…})(
    中。如果你用谷歌浏览器包装它,你会看到最后的调用也会产生一个错误。