eval究竟那里不好?

为何要罕用eval?

eval是 js 中一个壮大的要领。都说eval == evil即是true,这篇文章将钻研eval的几个瑕玷和运用注重事项。

目次

  • 一、平安性
  • 二、运转效力
  • 三、作用域
  • 四、内存▲
  • 五、总结和应对计划

一、平安性

太显著了,暂不议论

二、运转效力

都晓得 eval 比较慢,究竟慢若干,自身测测看,下面是代码(对照运转 1万次 eval("sum++") 和 500万次 sum++ 所须要的时候)

var getTime = function(){
  // return Date.now();
  return new Date().getTime()     //兼容ie8
}
var sum;
// 测试 1万次 eval("sum++")
sum = 0;
var startEval = getTime();
for(var i = 0;i<10000;i++){
  eval("sum++");
}
var durEval = getTime() - startEval;
console.log("durEval = ",durEval,"ms");
// 测试 500万次 sum++
sum = 0;
var startCode = getTime();
for(var i = 0;i<5000000;i++){
  sum++;
}
var durCode = getTime() - startCode;
console.log("durCode = ",durCode,"ms");
//输出效果
console.log('直接运转 sum++ 的速率约是 运转 eval("sum++") 的',(durEval * 500 / durCode).toFixed(0),'倍');

测试效果

在统一台PC上,测试3款浏览器和nodejs环境,效果以下:

Chrome 73

durEval =  236 ms
durCode =  14 ms
直接运转 sum++ 的速率约是 运转 eval("sum++") 的 8429 倍

Firefox 65

durEval =  766 ms
durCode =  167 ms
直接运转 sum++ 的速率约是 运转 eval("sum++") 的 2293 倍

IE8

durEval = 417ms
durCode = 572ms
直接运转 sum++ 的速率约是 运转 eval("sum++") 的365倍

Nodejs 10.15.0

durEval =  5 ms
durCode =  14 ms
直接运转 sum++ 的速率约是 运转 eval("sum++") 的 179 倍

Chrome 的 V8 果然是王者,Firefox 在运转eval的PK上输给了骨董IE8,node环境中eval的表现最好(只慢100多倍)

三、作用域

在作用域方面,eval 的表现让人费解。直接挪用时:当前作用域;间接挪用时:全局作用域

3.1 直接挪用

eval被直接挪用而且挪用函数就是eval自身时,作用域为当前作用域,function中的foo被修正了,全局的foo没被修正。

var foo = 1;
function test() {
    var foo = 2;
    eval('foo = 3');
    return foo;
}
console.log(test());    // 3
console.log(foo);       // 1

3.2间接挪用

间接挪用eval时 实行的作用域为全局作用域,两个function中的foo都没有被修正,全局的foo被修正了。

var foo = 1;
(function(){
  var foo = 1;
  function test() {
      var foo = 2;
      var bar = eval;
      bar('foo = 3');
      return foo;
  }
  console.log(test());    // 2
  console.log(foo);       // 1
})();
console.log(foo);         // 3

四、内存 ▲

运用eval会致使内存的糟蹋,这是本文要议论的重点。
下面用测试效果来对照,运用eval不运用eval 的状况下,以下代码内存的斲丧状况。

4.1 不必eval的状况

var f1 = function(){          // 建立一个f1要领
  var data = {
    name:"data",
    data: (new Array(50000)).fill("data 111 data")
    };                        // 建立一个不会被运用到的变量
  var f = function(){         // 建立f要领然后返回
    console.log("code:hello world");
  };
  return f;
};
var F1 = f1();

测试效果

在Chrome上检察内存运用状况,开发者东西->Momery->Profiles->Take snapshot,给内存拍个快照。

《eval究竟那里不好?》

为了便于查找,在过滤器中输入window,检察当前域的window

《eval究竟那里不好?》

能够看到,window占用了686122%的内存。然后在个中找F1变量:

《eval究竟那里不好?》

F1占用了320%的内存。

这好像申明不了什么。没有对照就没有危险,下面我们来危险一下eval

4.2 运用eval的状况

修正上面的代码,把 console.log 修正为 eval 运转:

- console.log("code:hello world");
+ eval('console.log("eval:hello world");');

测试效果

要领同上。在Chrome上检察内存运用状况,开发者东西->Momery->Profiles->Take snapshot

《eval究竟那里不好?》

window占用了2510484%的内存,个中F1占用了200140,相当于总量的3%的内存,F1.context.data,占用了200044,约即是F1的占用量,可见这些分外的内存开支都是来自于F1.context.data

4.3 剖析

运用eval时:F1占用了2001403%的内存;
不必eval时:F1占用了320%的内存;

如许的差异来自于javascript引擎的优化。在要领f1运转时建立了data,接着建立了一个要领ff中能够访问到data,但它没有运用data,然后f被返回赋值给变量F1,经由javascript引擎优化,这时候data不会被加入到闭包中,同时也没有其他指针指向datadata的内存就会被接纳。然而在f中运用了eval后,状况就差别了,eval太甚壮大,致使javascript引擎没法区分f会不会运用到data,从而只能将悉数的环境变量(包含data),一同加入到闭包中,如许F1就间接引用了datadata的内存就不会被接纳。从而致使了分外的内存开支。

我们能够进一步测试,这时候在开发者东西->Console 中输入:

F1 = "Hello"  //重设F1,如许就没什么引用到data了

然后用一样的要领检察内存,能够发明 window占用的内存,从200000+下落到了60000+

说到这里,再回头看eval新鲜的作用域。直接挪用时:当前作用域;间接挪用时:全局作用域,也就能够明白了。当间接挪用时,javascript引擎不晓得它是eval,优化时就会移除不须要的变量,假如eval顶用到了那些变量,就会发作意想不到的事变。这违犯了闭包的准绳,变得难以明白。干脆把间接挪用的作用域设置为了全局。

五、总结和应对计划

平安性

剖析:eval是不是平安重要由数据源决议,假如数据源不平安,eval只是供应了一种进击要领罢了。
计划:严厉管控数据源。

运转效力

剖析:eval比直接运转慢很多倍,但重要的斲丧在于编译代码历程,简朴项目中,不会如许高频率的运转eval
计划:低频运用时影响不大,不要高频运用,发起寻觅替换计划。

作用域

剖析:现实项目中直接挪用都很少,间接挪用更是少之又少。
计划:相识直接挪用和间接挪用的区分,遇到问题时不要懵逼即可。

内存

剖析:现实运用中很罕见,却很少有人会注重到内存治理,大项目中被重复运用会糟蹋较多的内存。
计划:优化编码范例,运用eval时注重那些没有被用到局部变量。

源码链接:github

    原文作者:千年虫
    原文地址: https://segmentfault.com/a/1190000018828386
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞