为什么不要使用eval以及with-编译原理+JS的再思考
结束这个学期有一段时间了,不得不说在家里的效率还是远远不如学校啊,失去了学校的大氛围之后,感觉自己变懒多了,以至于学期开始的时候比较懒散导致学期末疯狂的补,导致有半年我都没有什么更新了,在这里向还在看我博客的人道个歉,立个flag,接下来我要做一周至少三更的强者(FeiWu),好啦,进入正题,在之前的面试碰壁之后,我又拿起来我之前的书翻着看看,在这学期学了编译原理之后,对于JavaScript这本书中讲到的东西又是有了全新的理解。接下来就从编译原理的角度来分析题目吧。
首先,在编译原理中有一个很重要的概念,符号表,对于所有的编程语言来说,都会存在中间变量,而中间变量的存储方式是一元素指针表来保存的,这里借用一张陈瑾老师的PPT:
这张图片就是一段程序中所有的变量的储存方式,JavaScript也不例外。当代码开始编译的时候,编译器会将这些字段存入符号表中,供接下来的程序读写。对于JavaScript来说,假如在partition中查询某个变量,如果未找到,会顺着调用链到其父节点继续寻找,直到找到引用,或者未找到的话会报出异常(严格模式下)。
而eval函数的作用是通过字符串的形式动态执行代码,在代码运行之前编译器根本不知道需要分配如何的符号表,解决策略是每个eval函数会创建一张新的符号表,这样的话,对于编译优化时间本来就短的JavaScript来说会是雪上加霜,with函数的作用类似,在使用的时候,会对with内的变量创建一个全新的作用域,而且一旦有时内部出现了对象中未定义的变量时,如下:
obj = {
a:1,
b:2
}
with(obj){
a = 1;
b = 2;
c = 3;
}
console.log(obj.c) // = undifined
console.log(c) // = 3
不难看出,c这个变量由于在obj中一直没有找到,所以一直穿透到了全局变量中,这算是比较严重的数据泄露了,假设一下,假如说这是控制其他核心部件的对象,暴露到window由用户直接操作后果不堪设想。
除此之外,严格模式下对于eval和with来说不会创建新的符号表,也就是说eval中的变量无法传递给之后的变量,而with的变量无法穿透到全局,这看起来不错,貌似所有的危险都解决了,但这也恰恰是最危险的,因为你无法确定自己是在严格模式下还是普通模式下,这之间巨大的差异只会给程序带来麻烦,所以建议大家,尽量不要使用这两个方法。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。