以前在祼看ECMA262-5,在说到eval
的地方,死活看不明白为何会有一节专门扯到Direct Call to Eval
:javascript
A direct call to the eval function is one that is expressed as a CallExpression that meets the following two conditions:html
The Reference that is the result of evaluating the MemberExpression in the CallExpression has an environment record as its base value and its reference name is "eval".java
The result of calling the abstract operation GetValue with that Reference as the argument is the standard built-in function defined in 15.1.2.1.express
当时以为写规范的那群家伙必定是有毛病,众所周知,eval无非是接收一个字符串,把这个字符串当作代码解释执行。浏览器
这个问题我一直不明白,而后就放着。闭包
直到有一天.....函数
某一天,小伙伴们讨论到说关于闭包变量的问题时,Y君指出,若是一个函数没有引用到其所处闭包的a
变量,那这个a
变量所指向的空间将被释放。工具
function getClosure() { var a = 1; var b = 2; return function inner() { debugger; console.log(b); } } var func = getClosure(); func();
正如上面代码所示,当打开Chrome调试工具时到达debugger
语句时,咱们只能访问到b
变量。试图访问a
是会抛出ReferenceError的。性能
可是这真的能证实a
引用已经被释放了吗?若是没被释放的话,还不让在调试的时候访问,这个设计就只能说是(哔~)坑爹了,Y君补充道。测试
因而我写了以下代码作了测试:
function getClosure() { var a = 1; var b = 2; return function inner(val) { debugger; console.log(eval(val)); } } var func = getClosure(); func('a'); func('b');
在闭包中使用eval
,这样引擎就不知道我在闭包中的会引用到什么变量了。这一次到达debugger
语句时,惊奇地发现,a
和b
均可以直接引用到了。难道是由于eval
的缘由?引擎在发现闭包中有eval
以后,就不会回收闭包中的垃圾了?(由于它无从得知哪些变量会在未来被引用到)。
但若是引擎不知道未来会不会执行到eval呢?
function getClosure(){ var a = 1; var b = 2; return function inner(func, val) { debugger; console.log(func(val)); } } var f = getClosure(); f(eval, a);
把eval
当作函数传进去,而后让这个eval函数解释执行传入的val指向的变量。遗憾的是,到在debugger处,没法访问到a和b,执行到console.log(func(val))
抛了异常,表示找不到引用。
因而我才回过神,彷佛以前阅读过蛇精病般的什么direct call to eval
的东西,跟这个相关?
在规范中指出,进调用eval函数时:
this指向
,词法环境
以及变量环境
当作新的执行上下文的this指向
,词法环境
以及变量环境
。global对象
,全局词法环境
以及全局变量环境
当作新的执行上下文的this指向
,词法环境
以及变量环境
。那什么是直接调用呢?参照篇首。简言之,有以下限制:
a. BaseValue必须是一个Environment Record
b. Reference Name必须是"eval"
以下例子:
var a = eval; a('hello'); //非直接调用,由于Reference Name是'a'而不是'eval' var f = {eval: eval}; f.eval("hello"); //非直接调用,由于BaseValue是f变量,而不是Environment Record eval('hello'); //直接调用 function test() { var eval = window.eval, x = window.eval; eval('hello'); //直接调用 x('hello'); //非直接调用 }
看到规律了吧?只要是祼的eval
调用就是direct call,前面不要有宿主对象,且函数名必定要是eval
字符串。
了解了什么是直接eval调用后,若是我弄出这样的代码呢?
function getClosure() { var a = 1; var b = 2; var eval = function(x){ return x; } return function inner(val) { debugger; console.log(eval(val)); } } var func = getClosure(); func('a'); func('b');
在getClosure
中给一eval赋成一个彻底不相干的函数,进入到debugger时,能不能访问到a
和b
这两个变量呢?思考一下。
从eval能够管窥出来,ES5的规范几乎是从引擎现实者的角度来考虑问题。若是上下文中没有eval的话,那么闭包中的变量将被很好的释放掉(并不彻底正确,参见A surprising JavaScript memory leak found at Meteor),由于引擎能够检测出哪些变量会被引用到,而哪些不会。
对于ES3的引擎而言,如IE6,IE7, IE8,不管闭包中是否包含eval,它们都不会释放掉那些再也引用不到的变量。缘由在于,ES3规范中没有对非直接eval
调用进行规范,以下代码在IE6,7,8能经过,而在现代浏览器中会报错:
function getClosure(){ var a = 1; var b = 2; return function (func, val) { alert(func(val)); } } var f = getClosure(); f(eval, 'a'); f(eval, 'b');