非常教程

JavaScript参考手册

全球对象 | Global Objects

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)

JavaScript

JavaScript 是一种高级编程语言,通过解释执行,是一门动态类型,面向对象(基于原型)的解释型语言。它已经由ECMA(欧洲电脑制造商协会)通过 ECMAScript 实现语言的标准化。它被世界上的绝大多数网站所使用,也被世界主流浏览器( Chrome、IE、FireFox、Safari、Opera )支持。JavaScript 是一门基于原型、函数先行的语言,是一门多范式的语言,它支持面向对象编程,命令式编程,以及函数式编程。它提供语法来操控文本、数组、日期以及正则表达式等,不支持 I/O,比如网络