我有一个 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
因此,global.eval
适用于数学运算符,但无法访问变量i
,而eval
适用于两种情况。
这种行为是 V8 的限制吗?或者这是根据 ECMAScript 标准的预期行为?
最佳答案
是的,这是符合规范的行为。 ES5 15.1.2.1.1, Direct Call to Eval ,表示“直接”调用 eval
的一个要求是对 eval
的引用“有一个环境记录作为其基值。”这意味着它不能是通过属性访问完成的引用(在这种情况下,拥有的对象将是基值);它必须是一个“裸”函数。
这种区别对于 10.4.2, Entering Eval Code 的第 1 步至关重要:
- If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then,
- a. Initialise the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.
因此,对 eval
的间接调用被赋予全局变量环境,而不是局部变量环境。只有直接调用才能访问本地环境。
这样做是出于实际实现的原因,因为eval
可以向垃圾收集器发出信号,表明需要避免清理任何变量。例如,这是一个没有 eval
的案例:
function foo() {
var a = 5, b = 6, c = 7;
return function() { return a; }
}
var func = foo();
alert(func());
foo
返回的函数可能会在 foo
终止后访问 a
,但我们可以确定 b
和foo
终止后,c
将永远不会被再次访问。 b
和 c
可以安全地进行垃圾回收,而 a
则保持未回收状态。
现在是一个带有eval
的案例:
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 代码无法访问任何局部变量,从而避免了垃圾收集问题。
关于javascript - global.eval 无法访问词法范围内的变量。该行为是否符合 ECMAScript 标准?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31459180/