2019 年 9 月 25 日

Eval:运行代码字符串

内置的 eval 函数允许执行代码字符串。

语法为

let result = eval(code);

例如

let code = 'alert("Hello")';
eval(code); // Hello

代码字符串可以很长,包含换行符、函数声明、变量等。

eval 的结果是最后一条语句的结果。

例如

let value = eval('1+1');
alert(value); // 2
let value = eval('let i = 0; ++i');
alert(value); // 1

eval 后的代码在当前词法环境中执行,因此它可以看到外部变量

let a = 1;

function f() {
  let a = 2;

  eval('alert(a)'); // 2
}

f();

它还可以更改外部变量

let x = 5;
eval("x = 10");
alert(x); // 10, value modified

在严格模式下,eval 有自己的词法环境。因此,在 eval 中声明的函数和变量在外部不可见

// reminder: 'use strict' is enabled in runnable examples by default

eval("let x = 5; function f() {}");

alert(typeof x); // undefined (no such variable)
// function f is also not visible

如果没有 use stricteval 不会有自己的词法环境,因此我们会在外部看到 xf

使用 “eval”

在现代编程中,eval 的使用非常谨慎。人们常说 “eval 是邪恶的”。

原因很简单:很久很久以前,JavaScript 是一种非常弱的语言,许多事情只能通过 eval 来完成。但那个时代在十年前就过去了。

现在,几乎没有理由使用 eval。如果有人在使用它,他们很有可能可以用现代语言结构或 JavaScript 模块 来替换它。

请注意,它访问外部变量的能力有副作用。

代码缩小器(在 JS 进入生产之前用于压缩它的工具)将局部变量重命名为较短的变量(如 ab 等),以使代码更小。这通常是安全的,但如果使用了 eval,则不是,因为可以从 eval 的代码字符串中访问局部变量。因此,缩小器不会对所有可能从 eval 中可见的变量进行重命名。这会对代码压缩率产生负面影响。

eval 中使用外部局部变量也被认为是一种糟糕的编程实践,因为它使维护代码变得更加困难。

有两种方法可以完全避免此类问题。

如果 eval 的代码不使用外部变量,请将 eval 称为 window.eval(...)

这样,代码将在全局作用域中执行

let x = 1;
{
  let x = 5;
  window.eval('alert(x)'); // 1 (global variable)
}

如果 eval 的代码需要局部变量,请将 eval 更改为 new Function 并将其作为参数传递

let f = new Function('a', 'alert(a)');

f(5); // 5

new Function 结构在 “new Function” 语法 一章中有解释。它从一个字符串创建一个函数,也位于全局作用域中。因此,它看不到局部变量。但像上面的示例中那样,将它们显式地作为参数传递会更清晰。

总结

eval(code) 的调用会运行代码字符串并返回最后一条语句的结果。

  • 在现代 JavaScript 中很少使用,因为通常没有必要。
  • 可以访问外部局部变量。这被认为是不良做法。
  • 相反,要在全局作用域中 eval 代码,请使用 window.eval(code)
  • 或者,如果你的代码需要来自外部作用域的一些数据,请使用 new Function 并将其作为参数传递。

任务

重要性:4

创建一个计算器,提示输入一个算术表达式并返回其结果。

在这个任务中,不需要检查表达式的正确性。只需计算并返回结果即可。

运行演示

让我们使用 eval 来计算数学表达式

let expr = prompt("Type an arithmetic expression?", '2*3+2');

alert( eval(expr) );

不过,用户可以输入任何文本或代码。

为了保证安全,并且仅限于算术运算,我们可以使用 正则表达式 检查 expr,以便它只包含数字和运算符。

教程地图

评论

在评论之前请阅读此内容…
  • 如果您有改进建议 - 请 提交 GitHub 问题 或提交拉取请求,而不是发表评论。
  • 如果您无法理解文章中的某些内容 – 请详细说明。
  • 要插入几行代码,请使用 <code> 标签,对于多行代码 – 将它们包装在 <pre> 标签中,对于 10 行以上的代码 – 使用沙箱(plnkrjsbincodepen…)