在Javascript中eval是恶魔!MDN中关于eval的部分这样写道:
废除
该功能被废除。尽管现在浏览器依然支持此功能,在新的项目中并不鼓励这种用法。尽量避免使用它。
eval 会执行一段包含着代码的字符串,例如:
eval("var x = 'Hello from eval!';"); console.log(x);
eval会带来以下问题:
安全性:你的字符串能被第三方的Javascript库或者用户输入的内容注入。
调试:很难去调试错误 - 你将得不到发生错误的行数以及错误发生的点。
优化:Javascript解释器将不会预编译代码因为它随时可能改变。这样的做法通常比原生代码要运行的慢,即便是在解释器变得更有效率的前提下。
不幸的是,eval的功能非常强大,缺乏经验的开发者经常过度使用这个命令。
尽管有警告,eval任然在浏览器内继续工作 - 即使是在严格模式下 -但是你通常可以避免使用它。过去它主要用来解析JSON字符串但是现在我们有了更安全的JSON.parse方法。
然而,要是我们有一个包含函数名的字符串,比如说:
// 我们想要运行的函数
var fnstring = "runMe";
function runMe() {
// do stuff
}
我们怎么样在不使用eval的前提下执行 runMe() 函数呢?我最近在使用HTML5 History API的时候就遇到了这个问题;pushState方法并不允许你将一个指向存储为一个函数以你你需要将它的名字定义为一个字符串。当你使用Web Workers和其他类似的序列化对象API是也会遇到相同的挑战。
最简单最安全的方法是编写列条件,例如:
// 我们想运行的函数
var fnstring = "runMe";
switch (fnstring) {
case "functionX": functionX(); break;
case "functionY": functionY(); break;
case "functionZ": functionZ(); break;
case "runMe": runMe(); break;
}
这种方式很安全,但是当你有一堆函数需要调用时非常低效而且痛苦。
一种更好的解决方法是使用window对象,它可以指向当前窗口以及其中所有的项目。我们可以来检查window中的fnstring是否是一个对象,如果它是一个函数那么就执行它。例如:
// 我们想要执行的函数
var fnstring = "runMe";
// 发现对象
var fn = window[fnstring];
// 对象是不是函数
if (typeof fn === "function") fn();
如果需要确保函数有一个预想的名字,你也可以进行其他检查。
要是我们想要调用的函数有参数怎么办 - 也许储存在一个数组里?没问题,我们只需要使用apply方法:
// 函数名和传递的参数
var fnstring = "runMe";
var fnparams = [1, 2, 3];
// 发现对象
var fn = window[fnstring];
// 对象是函数
if (typeof fn === "function") fn.apply(null, fnparams);
阻止我们使用eval的理由多种多样。比起eval来,上面的方法更加安全,更少发生错误,更易调试,并且通常能更快执行。我希望本文对你会有帮助。
在文章下面的评论中,有人提供了另一个方法
setTimeout("runMe",0);