eval
eval
eval()
函数会将传入的字符串当做JavaScript代码进行执行。
语法
eval(string)
参数
string
一串表示JavaScript表达式,语句, 或者是一系列语句的字符串。表达式可以包括变量以及已存在对象的属性。
返回值
执行指定代码之后的返回值。如果返回值为空,返回undefined
描述
eval()是全局对象的一个函数属性。
eval()
的参数是一个字符串。如果字符串表示的是表达式,eval()
会对表达式进行求值。如果参数表示一个或多个JavaScript语句, 那么eval()
就会执行这些语句。注意不要用eval()
来执行一个四则运算表达式;因为 JavaScript 会自动为四则运算求值并不需要用eval来包裹。
如果要将算数表达式构造成为一个字符串,你可以用eval()在随后对其求值。比如,假如你有一个变量 x ,你可以通过一个字符串表达式来对涉及x的表达式延迟求值,将 "3 * x + 2
",存储为变量,然后在你的脚本后面的一个地方调用eval()。
如果eval()
的参数不是字符串,eval()
将会将参数原封不动的返回。在下面的例子中,字符串构造器被指定,eval()
返回了字符串对象而不是对字符串求值。
eval(new String('2 + 2') // returns a String object containing "2 + 2"
eval('2 + 2' // returns 4
你可以使用通用的的方法来绕过这个限制,如使用toString()
var expression = new String('2 + 2'
eval(expression.toString()
如果你间接的使用 eval(), 如通过一个引用来调用它而不是直接的调用eval。 从ECMAScript 5起它工作在全局作用域而不是局部作用域中;这就意味着,例如,下面的代码的作用声明创建一个全局函数,并且geval中的这些代码在执行期间不能在被调用的作用域中访问局部变量。
function test() {
var x = 2, y = 4;
console.log(eval('x + y') // Direct call, uses local scope, result is 6
var geval = eval; // equivalent to calling eval in the global scope
console.log(geval('x + y') // Indirect call, uses global scope, throws ReferenceError because `x` is undefined
(0, eval)('x + y' // another example of Indirect call
}
避免在不必要的情况下使用eval
eval()
是一个危险的函数, 他执行的代码拥有着执行者的权利。如果你用eval()
运行的字符串代码被恶意方(不怀好意的人)操控修改,您可能会利用最终在用户机器上运行恶意方部署的恶意代码,并导致您失去您的网页或者扩展程序的权限。更重要的是,第三方代码可以看到某一个eval()
被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的Function就是不容易被攻击的。
eval()的运行效率也普遍的比其他的替代方案慢,因为他会调用js解析器,即便现代的JS引擎中已经对此做了优化。
在常见的案例中我们都会找更安全或者更快的方案去替换他
访问成员属性
你不应该去使用eval()来将属性名字转化为对象的属性属性。考虑下面的这个例子,被访问对象的属性在它被执行之前都会未知的。这里虽然可以被处理用eval:
var obj = { a: 20, b: 30 };
var propName = getPropName( // returns "a" or "b"
eval( 'var result = obj.' + propName
但是,这里并不是必须得使用eval()。事实上,这里并不支持这样使用。可以使用属性访问器进行代替,它更快而且更安全:
var obj = { a: 20, b: 30 };
var propName = getPropName( // returns "a" or "b"
var result = obj[ propName ]; // obj[ "a" ] is the same as obj.a
你还可以使用这个方法去访问子代的属性。如下:
var obj = {a: {b: {c: 0}}};
var propPath = getPropPath( // returns e.g. "a.b.c"
eval( 'var result = obj.' + propPath
这里则通过规避使用Eval()实现了循环获取子代的属性:
function getDescendantProp(obj, desc) {
var arr = desc.split('.'
while (arr.length) {
obj = obj[arr.shift()];
}
return obj;
}
var obj = {a: {b: {c: 0}}};
var propPath = getPropPath( // returns e.g. "a.b.c"
var result = getDescendantProp(obj, propPath
同样的方法也可实现设置子代的属性值:
function setDescendantProp(obj, desc, value) {
var arr = desc.split('.'
while (arr.length > 1) {
obj = obj[arr.shift()];
}
obj[arr[0]] = value;
}
var obj = {a: {b: {c: 0}}};
var propPath = getPropPath( // returns e.g. "a.b.c"
var result = setDescendantProp(obj, propPath, 1 // test.a.b.c will now be 1
使用函数而非代码段
JavaScript拥有first-class functions,这意味着你可以将函数直接作为参数传递给其他接口,并将他们保存在变量中或者对象的属性中等等,很多DOM的API都用这种思路进行设计,你也可以(或者应该)这样子设计你的代码:
// instead of setTimeout(" ... ", 1000) use:
setTimeout(function() { ... }, 1000
// instead of elt.setAttribute("onclick", "...") use:
elt.addEventListener('click', function() { ... } , false
Closures 也可以作为一种创建参数化函数而不连接字符串的方法.
解析 JSON(将字符串转化为JavaScript对象)
如果你在调用eval()传入的字符串参数中更包含数据(如:一个数组“[1,2,3]”)而不是代码,你应该考虑将其转换为JSON对象,这允许你用JavaScript语法的子集来表示数据。在扩展中下载JSON和JavaScript
提示:因为JSON语法子集相对于JavaScript语法子集比较有局限性,很多在JavaScript中可用的特性在JSON中就不起作用了,如:后缀都好JSON就不支持,并且,对象中的属性名在JSON中必须用引号括起来,所以在使用JSON序列化将字符串转化为JSON对象时需确认字符串格式
尽量传递数据而非代码
例如,设计用于擦除网页内容的扩展可能会在XPath中定义规则,而不是JavaScript代码。
执行这段代码应加以权限限制
如果你必须执行这段代码, 应考虑以更低的权限运行. 这建议主要应用于Components.utils.evalInSandbox 可扩展的标记语言
例子
举例: 使用eval
在下面的代码中,两种声明都返回了42
。第一种是对字符串 "x + y + 1
"求值;第二种是对字符串 "42
"求值。
var x = 2;
var y = 39;
var z = '42';
eval('x + y + 1' // returns 42
eval(z // returns 42
举例: 使用 eval 对JavaScript声明求值
下面的例子使用eval()
对str字符串求值。这个字符串包含了JavaScript声明,如果x等于5,就打开一个Alert 对话框,然后对 z 赋值。 否则就对z赋值0。 当第二个声明被执行, eval 将会将str表达式执行,然后会对声明集合求值,并将返回值赋值给z。
var x = 5;
var str = "if (x == 5) {console.log('z is 42' z = 42;} else z = 0;";
console.log('z is ', eval(str)
最后的表达式被计算
eval
将会返回对最后一个表达式的求值结果。
var str = 'if ( a ) { 1 + 1; } else { 1 + 2; }';
var a = true;
var b = eval(str // returns 2
console.log('b is : ' + b
a = false;
b = eval(str // returns 3
console.log('b is : ' + b
eval 作为字符串定义函数需要“(”和“)”作为前缀和后缀
var fctStr1 = 'function a() {}'
var fctStr2 = '(function a() {})'
var fct1 = eval(fctStr1) // return undefined
var fct2 = eval(fctStr2) // return a function
规范
Specification | Status | Comment |
---|---|---|
ECMAScript 1st Edition (ECMA-262) | Standard | Initial definition. |
ECMAScript 5.1 (ECMA-262)The definition of 'eval' in that specification. | Standard | |
ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'eval' in that specification. | Standard | |
ECMAScript Latest Draft (ECMA-262)The definition of 'eval' in that specification. | Living Standard | |
浏览器兼容性
Feature | Chrome | Edge | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|---|
Basic support | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) |
Feature | Android | Chrome for Android | Edge | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|---|
Basic support | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) | (Yes) |