本文深入解析JavaScript中eval()函数与with语句的执行机制。eval将字符串作为代码执行,影响作用域并导致性能下降;with简化深层对象访问但易引发变量泄漏。在严格模式下,eval创建变量的能力受限,而with被完全禁止。文章通过代码示例说明其副作用及最佳实践建议。
eval与with概述
前言
eval和with在JavaScript开发中常受诟病,仿佛它们的存在本身就是一种错误。这类似于CSS领域中,表格若仅用于展示数据而非布局,虽被指责为不规范,但有时也属于矫枉过正。那么,eval和with的真实情况究竟如何?本文将深入剖析eval()函数与with语句的具体行为。
eval函数详解
eval函数
定义
eval()是JavaScript的全局函数,用于解释并执行由JavaScript源代码组成的字符串。
var result = eval('3+2');
console.log(result,typeof result);//5 'number'
用法
eval()仅接受一个参数。若传入的参数非字符串,则直接返回该参数。若参数为字符串,引擎会尝试将其编译为JavaScript代码:编译失败时抛出SyntaxError异常;编译成功后则执行该代码,并返回字符串中最后一个表达式或语句的值;若最后无返回值,则返回undefined。若字符串执行过程中抛出异常,该异常将向上传递给eval()调用处。
var num = 1;
var str = 'test';
console.log(eval(num));//1
console.log(eval(str));//ReferenceError: test is not defined
var strLong1 = 'var x = 1;var y = 2;';
console.log(eval(strLong1),x,y);//undefined 1 2
var strLong2 = 'var x = 1; x++;';
console.log(eval(strLong2),x);//1 2
作用域
eval()在执行时,会复用调用它时的变量作用域环境。这意味着,在eval中查找变量值或定义新变量、函数的行为,与在局部作用域中直接编写代码完全一致。
var b = 2;
function foo(str,a){
eval(str);
console.log(a,b);
}
foo('var b = 3;',1);//1 3
别名调用
当通过别名(如将eval赋值给其他变量)调用时,eval()会将传入的字符串视为顶层全局代码执行。此时,代码可以定义新的全局变量或全局函数,或修改全局变量,但无法访问或修改调用函数内部的局部变量。
var geval = eval;
var x = 'global',y = 'global';
function f(){
var x = 'local';
eval('x += "changed";');
return x;
}
function g(){
var y = 'local';
geval('y += "changed";');
return y;
}
console.log(f(),x);//localchanged global
console.log(g(),y);//local globalchanged
[注意]在IE8及更低版本的浏览器中,通过别名调用eval()的结果与直接调用eval()相同,即仍遵循局部作用域规则。
副作用
JavaScript解释器会对代码进行大量分析与优化。然而,eval()动态执行的代码通常无法被静态分析,导致解释器无法对其进行优化,从而引起性能显著下降。
与eval()类似的机制包括setTimeout()、setInterval()以及new Function()构造函数,它们均接受字符串参数并在运行时动态执行。这种动态执行机制带来的便利性往往无法弥补其性能损耗,因此应尽量避免使用。
严格模式
鉴于eval()功能过于强大且潜在风险高,JavaScript严格模式对其施加了严格限制。
【1】在严格模式下,不能通过eval()创建变量或函数,但仍可查询和修改已存在的全局变量值。
'use strict';
eval('var x = 1;');
console.log(x);//ReferenceError: x is not defined
'use strict';
var x = 1;
eval('x = 2;');
console.log(x);//2
【2】严格模式下禁止将eval作为标识符(变量名或函数名)。
'use strict';
var eval = 10;//SyntaxError: Unexpected eval or arguments in strict mode
with语句机制
with语句
定义
with语句的主要目的是简化对同一对象多次属性访问的代码编写。
with(object){
statement;
}
作用
当对象嵌套层级极深时,开发者常借助 with 语句来精简代码逻辑。其核心机制是将对象引用视作作用域,把对象属性映射为作用域内的标识符,进而生成一个全新的词法作用域。
在对象嵌套层级较深时,通常使用with语句来简化代码。其本质是将对象引用作为作用域处理,将对象属性视为作用域中的标识符,从而创建一个新的词法作用域。
document.forms[0].address.value
在客户端JavaScript开发中,常需通过类似document.forms[0].address.value的表达式访问HTML表单元素。
with(document.forms[0]){
name.value = '';
address.value = '';
emai.value = '';
}
若此类表达式在代码中频繁出现,可使用with语句将form对象提升至作用域链顶层。这种方法大幅减少了输入代码量,无需为每个属性名重复添加document.forms[0]前缀。该对象临时挂载于作用域链上,当JavaScript解析如address等标识符时,会自动在此对象中查找。
[注意]with语句提供了一种读取对象属性的快捷方式,但它并不能创建对象的属性。
若对象o已拥有属性x,以下代码会将该属性值赋为1。
var o = {x:0};
with(o) x = 1;
console.log(o.x);//1
若对象o中未定义属性x,以下代码与不使用with语句的x=1行为完全一致。这是因为对变量x进行了LHS(左值)查询,并将1赋值给该变量。
var o = {};
with(o) x = 1;
console.log(o.x);//undefined
console.log(x);//1
副作用
与eval类似,with语句的代码极难优化,且会增加调试难度。相比不使用with的代码,其运行速度更慢。
此外,若with语句使用不当,可能导致变量泄漏,进而污染全局作用域。
var x = 1;
var o = {};
with(o){
x = 2;
}
console.log(x);//2
console.log(o.x);//undefined
严格模式
在严格模式下,禁止使用with语句。
//SyntaxError: Strict mode code may not include a with statement
'use strict';
var o = {};
with(o){
x = 2;
}
总结与建议
总结
使用eval和with会阻碍引擎在编译阶段对作用域查找进行优化,导致性能下降及代码运行变慢。由于eval和with在实际工作中极少使用,严格模式下的限制对大多数开发者影响甚微。这好比某国宣布不再发放前往某地的签证,虽该地知名,但多数人此生未必前往,故无需过度担忧。同理,eval和with是否被嫌弃,在实际应用中已无关紧要。
以上所述是小编给大家介绍的JavaScript学习小结之被嫌弃的eval函数和with语句实例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!