作为解释性语言,JavaScript同样可以解释运行由JavaScript源代码组成的字符串,并产生一个值。实现的方式就是全局函数eval()。
eval("3+2") // => 5
JavaScript语言的早期版本定义了eval()函数,eval()只有一个参数。
如果传入的参数不是字符串,则直接返回这个参数;如果是字符串,则会当成JavaScript代码进行编译,如果编译失败则会抛出一个语法错误(SyntaxError)异常,如果编译成功,则开始执行这段代码,并返回字符串中的最后一个表达式或语句的值(没有值,则返回undefined)。
重点是,eval()函数使用了调用它的变量作用域环境,即eval()查找变量的值和定义新变量/函数的操作跟局部作用域中的代码完全一样,即局部eval()。例如:
var foo = function () {
var x = 0;
eval("x = 1");
return x;
}
foo(); // => 1
同样地,eval()函数可以通过如下代码声明一个局部函数:
eval("function f() { return x + 1; }");
当然,如果在最顶层代码中调用eval(),它会作用于全局变量和全局函数,即全局eval()。
eval()具有更改局部变量的能力,这对于JavaScript优化器来说是一个很大的问题。因为用于动态执行的代码通常来讲是不能分析的,一般来讲,如果一个函数调用了eval(),那么JavaScript解释器将无法对这个函数做进一步优化。按照这个逻辑,如果将eval()赋值给一个变量g,那么解释器将无法优化任何调用g()的函数。
ECMAScript3不允许为eval()设置变量名,如果eval()函数通过变量名调用的话,则会抛出一个EvalError异常。
但实际上大多数的实现并不是这么做的。当通过变量名调用时,eval()会被当成全局eval()。
ECMAScript 5是反对使用EvalError的,并且规范了eval()的行为。“直接的eval(direct eval)”调用(直接使用“eval”调用,eval看起来更像一个保留字),则被当成局部eval();使用变量名调用,则被当成全局eval()。
IE9之前的版本和其他浏览器不同,当通过变量名调用eval()时并不是全局eval(),也不会抛出EvalError异常,而是仅仅当作局部eval()来调用。但IE定义了一个名叫execScript()的全局函数来完成全局eval()的功能(但execScript()总是返回null)。
ECMAScript 5 严格模式(参考上一篇文章)对eval()函数的行为施加了更多限制,甚至对标识符eval的使用也施加了限制。
在严格模式下,或者eval()执行的代码段以"use strict"指令开始,这里的eval()是局部eval(),并且不能再局部作用域中定义新的变量或函数。
严格模式将“eval”列为保留字,不能用一个变量名来覆盖eval()函数,并且变量名、函数名、函数参数或者异常捕获的参数都不能取名为“eval”。