global.eval 无法访问词法范围内的变量。该行为是否符合 ECMAScript 标准?
global.eval is not able to visit variables in the lexical scope. Does the behavior comply ECMAScript standard?
我有一个 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
为 "direct" 的一个要求是对 eval
的引用“ 具有环境记录作为其基值。”这意味着它不能是通过 属性 访问完成的引用(在这种情况下,拥有对象将是基值);它必须是一个 "bare" 函数。
这个区别对于 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
和 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 代码无法访问 any局部变量,从而避免垃圾回收问题。
我有一个 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
为 "direct" 的一个要求是对 eval
的引用“ 具有环境记录作为其基值。”这意味着它不能是通过 属性 访问完成的引用(在这种情况下,拥有对象将是基值);它必须是一个 "bare" 函数。
这个区别对于 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
和 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 代码无法访问 any局部变量,从而避免垃圾回收问题。