eval is evil!

http://www.guhaodi.com/2014/06/29/eval-is-evil/


eval 函数传递一个字符串给 JavaScript 编译器,并且执行其结果.

上面是 JavaScript eval() 函数的定义.语法如下:

eval( string )
用法说明:
  1.  eval() 传递一个包含一个或者多个表达式的字符串, eval() 会调用 JavaScript 编译器,计算并返回表达式的值.
  2. 不用给 eval() 传递一个只包含算术表达式的字符串,因为 JavaScript 会自动计算算术表达式.
  3. 如果 evla() 的参数不是一个字符串,那么,函数不会对参数做任何改变,并且原封不动地返回它.

从上面的介绍看,貌似 eval() 的功能还蛮强大的,它能执行任何传入的 JavaScript 表达式!
那么,为什么大家都建议代码里不要使用 eval() ,甚至不适用它都已经成为一个标准(JSLint中不允许出现eval())了.
主要有下面两个原因:

  1. 安全性太差.尽管 eval() 功能很强大,但恰恰因为它能执行传入的 JavaScript 表达式,并且用户通常能使用第三方插件找到你的代码中什么地方调用了 eval() ,这样就极大增加了你被攻击的可能性.
  2. 性能低. eval() 在执行的时候需要调用 JavaScript 编辑器,这会影响程序的性能.JSON.parse vs eval 解析 JSON 的性能对比

基于以上原因, eval() 并不受开发者欢迎.甚至,国外的 JavaScript 大师 Douglas Crockford(JSON 和 JSLint 都是他提出和创建的) 在他的畅销书 JavaScript: The Good Parts(JavaScript 语言精粹)中明确把 eval() 列为 JavaScript 语言中的糟粕…而且在他的编程规范里禁用 eval() .

那么,既然我们要尽量不使用 eval() ,又有哪些方式可以代替它呢?
可以针对 eval() 常用的情形给出下面两个方法:

1. 解析 JSON 的时候

我以前在处理一个 JSON 字符串的时候,如果要把它解析成一个对象,通常会使用 eval(‘(‘ + jsonStr + ‘)’) 的方式解析.
在了解到 eval() 的可怕后,开始改用 JSON.parse (对应的,还有 JSON.stringify 方法用来解析成JSON字符串)的方法.

JSON.parse 的优点:

  1. 只是把 JSON 字符串解析成对象,不执行表达式,没有像 eval() 那样的安全性问题.
  2. 不调用 JavaScript 编译器,性能完爆 eval()

JSON.parse 的缺点: 只有主流浏览器支持原生 JSON 对象,老的,过时的浏览器没有这个方法.

所以,要想在解析 JSON 的时候,用 JSON.parse 完全取代 eval() ,你必须解决浏览器支持的问题.

Note: 事实上, JSON.parse 是基于 Douglas Crockford 对 JSON 的安全解析方法.JSON.parse 的方法中也使用了 eval(),只不过在使用 eval() 之前,验证了传入 eval() 的字符串是一个合法,安全的字符串,这使得 JSON.parse 能够安全解析 JSON.

为了解决这个问题,推荐你直接引用 Douglas Crockford 在 GitHub 上分享的解决方案,选一个作为你的代码的依赖文件.
或者,也可以自己写一个模拟的 JSON 对象,一个 polyfill:

if (!window.JSON) {
  window.JSON = {
    parse: function (sJSON) { return eval("(" + sJSON + ")"); },
    stringify: function (vContent) {
      if (vContent instanceof Object) {
    var sOutput = "";
    if (vContent.constructor === Array) {
      for (var nId = 0; nId < vContent.length; sOutput += this.stringify(vContent[nId]) + ",", nId++);
        return "[" + sOutput.substr(0, sOutput.length - 1) + "]";
    }
    if (vContent.toString !== Object.prototype.toString) { 
      return "\"" + vContent.toString().replace(/"/g, "\\$&") + "\"";
    }
    for (var sProp in vContent) { 
      sOutput += "\"" + sProp.replace(/"/g, "\\$&") + "\":" + this.stringify(vContent[sProp]) + ",";
    }
    return "{" + sOutput.substr(0, sOutput.length - 1) + "}";
     }
     return typeof vContent === "string" ? "\"" + vContent.replace(/"/g, "\\$&") + "\"" : String(vContent);
    }
  };
}

上面这段代码是从这里搬运来的.只要把它放在你代码的最前面就好了.
可惜的是,这段代码还是使用了 eval() ,而且没有对传入的字符串加任何安全验证.效果跟直接使用 eval() 是一样的.
再或者,你如果放弃治疗的话,还可以这样…

if(window.JSON){

  // 用 JSON.parse
}else{

  // 用 eval()
}
2. 使用类似 eval() 的方法的时候

JavaScript 中还有其他函数,比如 Function()构造函数, setTimeout() 和 setInterval() 也跟 eval() 一样会执行字符串中的表达式.
比如:

setTimeout('window.location.reload()',1000); // 1秒后刷新页面

上面的代码中 setTimeout() 执行了字符串中的代码.实际上跟 eval() 是一样的.
为了解决这个问题,可以用函数来代替字符串传入 setTimeout() :

setTimeout(function(){
  window.location.reload(); // 同样能在1秒后刷新页面
},1000);

总结

eval() 低效又不安全,能不用的话,尽量就别用它吧~改成用上面的方式取代它.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值