JavaScript中的eval()函数详解
和其他很多解释性语⾔⼀样,JavaScript同样可以解释运⾏由JavaScript源代码组成的字符串,并产⽣⼀个值。JavaScript通过全局函数eval()来完成这个⼯作
eval(“1+2”),-> 3
动态判断源代码中的字符串是⼀种很强⼤的语⾔特性,⼏乎没有必要在实际中应⽤。如果你使⽤了eval(),你应当仔细考虑是否真的需要使⽤它。
⼀、eval()是⼀个函数还是⼀个运算符
eval()是⼀个函数,但由于它已经被当成运算符来对待了。。JavaScript语⾔的早期版本定义了eval函数,现代JavaScript解释器进⾏了⼤量的代码分析和优化。⽽eval的问题在于,⽤于动态执⾏的代码通常来讲不能分析,换句话说,如果⼀个函数调⽤了eval,那么解释器将⽆法对这个函数做进⼀步优化,⽽将eval定义为函数的另⼀个问题是,它可以被赋予其他的名字,var f=eval;那么解释器就⽆法放⼼的优化任何调⽤了f()的函数。⽽当eval是⼀个运算符的时候,就可以避免这些问题。
⼆、eval()
eval()只有⼀个参数。如果传⼊的参数不是字符串,它直接返回这个函数。如果参数是字符串,它会把字符串当成JavaScript代码进⾏编译,如果编译失败者抛出⼀个语法错误异常。如果编译成功,则开始执⾏这⼀段代码,并返回字符串中的最后⼀个表达式会或语句的值,如果最后⼀个表达式或语句没有值,则最终返回undefined。如果字符串抛出⼀个异常,这个异常将把该调⽤传递给eval()。
javascript 函数关于eval最重要的是,它使⽤了调⽤它的变量作⽤域环境。也就是说,它查变量的值和定义新变量和函数的操作和局部作⽤域中的代码完全⼀样。如果⼀个函数定义了⼀个局部变量x,然后调⽤eval(“x”),它会返回局部变量的值。如果它调⽤eval(“x=1”),它会改变局部变量的值。如果函数调⽤了eval(“var y=2;”),它声明了⼀个新的局部变量y,同样地,⼀个函数可以通过如下代码声明⼀个局部变量:
eval(“function f(){return x+1;}”);
如果在最顶层的代码中调⽤eval,当然,它会作⽤于全局变量和全局函数。
需要注意的是,传递给eval的字符串必须在语法上将的通,不能通过eval往函数中任意粘贴代码⽚段,⽐如:eval(“return ;”)是没有意义的,因为return只有在函数中才起到作⽤,并且事实上,eval的字符串执⾏时的上下⽂环境和调⽤函数的上下⽂环境是⼀样的,这不能使其作为函数的⼀部分来运⾏。如果字符串作为⼀个单独的脚本是有语义的,那么将其传递给eval作参数是完全没有问题的,否则,eval会抛出语法错误异常。
三、全局eval()
eval()具有更改布局变量的能⼒,这对于JavaScript优化器来说是⼀个很⼤的问题。然⽽作为⼀种权宜之计,JavaScript解释器针对那些调⽤了eval的函数所做的优化并不多。但当脚本定义了eval的⼀个别名,且⽤另⼀个名称调⽤它,JavaScript解释器⼜会如何⼯作呢?为了让JavaScript解释器的实现更加简化,ECMAScript3标准规定了任何解释器都不允许对eval赋予别名。如果eval函数通过别名调⽤的话,则会抛出⼀个EavlError异常。
实际上,⼤多数的实现并不是这么做的。当通过别名调⽤时,eval会将其字符串当成顶层的全局代码来执⾏。执⾏的代码可能会定义新的全局变量和全局函数,或者给全局变量赋值,但却不能使⽤或者修改主调函数中的局部变量,因此,这不会影响到函数内的代码优化。
ECMAScript5是反对使⽤EavlError的,并且规范了eval的⾏为,“直接的eval”,当直接使⽤⾮限定的“eval”名称来调⽤eval()函数时,通常称为“直接eval”。直接调⽤eval()时,它总是在调⽤它的上下⽂作⽤域内执⾏。其他的间接调⽤则使⽤全局对象作为其上下⽂作⽤域,并且⽆法读、写、定义局部变量和函数。下⾯有⼀段⽰例代码:
var geval=eval; //使⽤别名调⽤evla将是全局eval
var x="global",y="global"; //两个全局变量
function f(){ //函数内执⾏的是局部eval
var x="local"; //定义局部变量
eval("x += ' chenged';");//直接使⽤eval改变的局部变量的值
return x; //返回更改后的局部变量
}
Function g(){ //这个函数内执⾏了全局eval
var y="local";
geval("y += ' changed';"); //直接调⽤改变了全局变量的值
return y;
}
console.log(f(),x); //改变了布局变了,输出 “local changed global”
console.log(g(),y); //改变了全局变量,输出 “local global changed”
全局的eval的这些⾏为不仅仅是处于代码优化其的需要⽽作出的⼀种折中⽅案,它实际上是⼀种⾮常有⽤的特性,它允许我们执⾏那些对上下⽂没有任何依赖的全局脚本代码段。真正需要eval来执⾏代码段的场景并不多见。但当你真的意识到它的必要性的时候,你更可能会使⽤全局eval⽽不是局部eval。
四、严格eval()
ECMAScript5严格模式对eval()函数的⾏为施加了更多的限制,甚⾄对标识符eval的使⽤也施加了限制。当在严格模式下调⽤eval时,或者eval执⾏的代码段以“Use strict” 指令开始,这⾥的eval是私有上下⽂环境中的局部eval。也就是说,在严格模式下,eval执⾏的代码段可以查询或更改局部变量,但不能在局部作⽤域中定义新的变量或函数。
此外,严格模式将“eval”列为保留字,这让eval()更像⼀个运算符。不能⽤⼀个别名覆盖eval()函数。并且变量名,函数名。函数参数或者异常捕获的参数都不能取名为eval。
俗话说的好:宝剑锋从磨砺出,梅花⾹⾃苦寒来。
出处:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论