eval() 是 JavaScript 中一个很是有用的函数,它能够一段代码字符串动态执行。然而各类编码规范和最佳实践都强烈抵制 eval,几乎将 eval 打入了死牢,大牛 Douglas Crockford 也在《JavaScript 语言精粹》一书中将 eval 视为 JavaScript 中糟粕。这篇文章将带你们从新认识这个函数,知道为何不用它,以及为何不得不用它。node
在分析 eval 的利弊前,首先来认识一下它。在不清楚一项技术的状况下,就对它作出武断地评价,是有失公允的。git
eval 是全局对象上的一个函数,会把传入的字符串当作 JavaScript 代码执行。若是传入的参数不是字符串,它会原封不动地将其返回。eval 分为直接调用和间接调用两种,一般间接调用的性能会好于直接调用。github
直接调用时,eval 运行于其调用函数的做用域下;正则表达式
var context = 'outside';
(function(){
var context = 'inside';
return eval('context');
})();
// return 'inside'
复制代码
而间接调用时,eval 运行于全局做用域。json
var context = 'outside';
(function(){
var context = 'inside';
geval = eval;
return geval('context');
// 下面两种也属于间接调用
// return eval.call(null, 'context');
// return (1, eval)('context');
})();
// return 'outside'
复制代码
所以,间接调用时,eval 并不会修改调用函数做用域内的任何东西。JS 解释器有 fast path 和 slow path 两种模式,当直接调用 eval 时,解释器处于 slow path。由于此时做用域是不可控的,须要监听整个做用域,不能应用 v8 的一些编译优化,相应的编译效率也会比 fast path 低。小程序
你们抵制 eval 的缘由主要是如下几个缘由:浏览器
鉴于以上各类缘由,不少人说 eval 是 evil(魔鬼)。另外,eval 还有一些难兄难弟,好比 new Function, setTimeout, setInterval。它们也具有执行一段代码字符串的能力。 究其本质缘由,仍是由于 JS 赋予这个方法的权限太大了,做为新手很难驾驭它,若是对 eval 没有很好地理解,很容易写出问题来。这有点像 C 语言中 goto 语句,一样是由于权限太大而被封杀的典范。安全
事实上,eval 一直在被误解,它多是最强大的一个 JavaScript 函数,但却由于一些人的误用,而被开发者们打入了冷宫。接下来,我来根据上述被质疑最多的几个点,给出一点本身的见解。bash
从测试结果可知,eval 的确会拖慢函数执行性能,并且随着函数规模增大,性能也越慢。可是在通常状况下(N < 1000000),性能差别并无 10 倍那么夸张。框架
虽然你们嘴上说不要用,可是 eval 用起来倒是真香。
<div data-eval="data.count = data.count + 1">
{{data.count}}
</div>
复制代码
渲染出来的结果是 eval 计算后的值。
不少库和框架都用了 eval 实现各类黑魔法。早期的有用 eval 解析 json 的,好比 Douglas Crockford 的 json2.js(真香!)。到后来,各类 MVVM 框架也用 new Function 这个 eval 的好基友,来实现模板内嵌表达式的计算,好比 Vue 和 avalon。要达到的效果和笔者上面介绍的例子大体相同,不一样的是这些 MVVM 框架还须要先解析模板,基于正则表达式提取出 new Function 的参数。
甚至不能用 eval 的时候,也要本身造一个 eval 出来。好比小程序上就不能使用 eval 和 new Function,那么若是想动态注入并执行代码的话,须要绕一个大弯,从编译原理出发,自行实现一个 JS parser。
关于 eval,笔者我的的见解是,你能够不去用它,但要去了解它。写这篇文章的目的也不是为了推荐你们使用 eval。就平时的业务开发而言,eval 几乎没有用武之地。但在一些特殊场合,eval 就像一枚核弹,无往不利。
参考连接: