eval(…)
eval(…)函数可以接受一个字符串作为参数,从而执行字符串参数组成的JavaScript代码。
eval("alert('Hello world!')")
上面代码运行会在浏览器窗口弹出"Hello world!"。
eval(…)函数通常被用来执行动态创建的代码,比如程序中逻辑生成或者从服务端获取的动态字符串JavaScript代码,这看上起很酷,但是不到万不得已不要使用它。
eval(…)函数坏处如下:
会对所处的词法作用域作出修改
可能会执行恶意JavaScript代码
带来的好处无法抵消性能上的消耗
会对所处的词法作用域作出修改
function render(str,a){
eval(str);
console.log(a,b) // 1 3
}
var b = 2;
render('var b = 3',1);
按照预期,程序应该输出的是1和2,但是这里输出了1和3,对于a没有什么好说的。但是输出3就有点让人费解。
其根本原因是因为eval函数修改了render函数词法作用域,在render函数内部创建了一个变量b,并遮蔽了外部(在这里是全局)作用域中的同名变量b,所以就输出了3。
可能会执行恶意JavaScript代码
function render(str,a){
eval(str);
console.log(a,b)
}
var b = 2;
render('for(;;);',1);
运行上述代码,会进入一个死循环,从而导致应用程序奔溃。在这里测试的时候我们传入eval的参数仅仅是一个死循环for的字符串,如果传入的字符串是网站攻击者别有用心处理过的,那么危害就比应用程序崩溃就大的多了。
带来的好处无法抵消性能上的消耗
使用eval(…)函数是有额外的性能消耗的,在JavaScript代码运行的同时必须启动一个新的解析器来解析这些字符串代码,启动新的解析器是有一定的开销的。
除了eval(…)函数,JavaScript中还有几种函数当接受字符串为参数的时候,也会启用额外的解析器,带来性能消耗。
var render = new Function("console.log('new Function')");
render();
setTimeout("console.log('setTimeout')", 500);
setInterval("console.log('setInterval')", 500);
应当避免上面使用方式,可以适当修改代码来获得性能上的提升。
// var render = new Function(func1); 不要使用
// render();
function func1() {
console.log('setTimeout')
}
function func2() {
console.log('setInterval')
}
setTimeout(func1, 500);
setInterval(func2, 500);
with
with语句简化了对同一个对象相关属性访问的操作。
var person = {
name:"render",
age:21,
sex:"男"
};
// 重复使用person进行属性的访问
console.log(person.name,person.age,person.sex);
//简化对person属性的访问
with (person) {
console.log(name,age,sex);
}
上面两个console.log语句输出完全一致,第一个输出语句没有什么好解释的,第二个输出语句是因为with将可以将一个没有或有多个属性的对象处理为一个完全隔离的词法作用域,对象的属性会被处理为在这个作用域的词法标识符,所以可以读取相关属性值。
但是并不推荐使用with语法来开发应用程序,因为with语法水太深,你把握不住。
//简化对person属性的访问
//简化对person属性的访问
with (person) {
var a = 3;
job = '前端工程师';
age = 25;
console.log(name, age, sex);
console.log(a);
console.log(job); // 前端工程师
console.log(person.job); // undefined
console.log(age);
}
console.log(a); // 3
console.log(person.job) // undefined
console.log(person.age) // 25
先不看var a = 3 语句,之前我们知道了with语句会创建一个全新的词法作用域,在该作用域中person中没有和job相符的标识符(可以理解为该作用域下person没有job属性),所以job输出’前端工程师’,person.job为’undefined’。
在该作用域下存在和person.age属性相符的标识符,所以age和person.age输出都为25。
在看var a = 3 语句,尽管with语句可以将一个对象处理为词法作用域,但是内部正常的var声明并不会被限制在该作用域中,而是被添加到with语句所处的函数作用域中。(此处是全局)
总结
JavaScript引擎会在编译阶段进行数项的性能优化,其中有些优化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。而引擎在词法分析阶段无法明确知道eval(…)会接收到什么代码,这些代码会如何对作用域进行修改,也无法知道传递给with语句来创建新词法作用域的对象的内容到底是什么。这两个机制导致JavaScript引擎无法在编译时对作用域进行查找优化。
总之就是一句话,不要或者尽量避免使用它们,哪怕是在严格模式下。(在严格模式下,使用with语句会报错,使用eval(…)不会修改当前词法作用域,但还是存在安全问题)