我有一个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, 对Eval的直接调用,说对eval
的调用是"直接"的一个要求是对eval
的引用"有一个环境记录作为其基础值。"这意味着它不能是由属性访问完成的引用(在这种情况下,拥有对象将是基值);它必须是一个"裸"函数。
这个区别对于10.4.2的第1步至关重要,输入Eval代码:
因此,对
- 如果没有调用上下文或者eval代码没有被eval函数的直接调用(15.1.2.1.1)求值,那么,
- 。如10.4.1.1所述,使用C语言的eval代码初始化执行上下文,就好像它是一个全局执行上下文一样。
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
和c
在foo
终止后永远不会再被访问。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代码不能访问任何局部变量,从而避免了垃圾收集问题。