eval(...)

492 阅读2分钟

开发过程中遇到一个问题:将字符串'false'和'true'转为布尔值

假设这个变量名为 stringBoolean,值为‘true’,我第一时间想到的是利用严格等号:

stringBoolean === 'true' // true

但是这种方法略显low,于是往下探索,再遇差点被遗忘在我脑海中的欺骗词法机制--eval

用eval(...)可以帮我达到转换字符串为布尔值的目的

eval('true')  // true

了解eval

eval(...)函数接收一个字符串为参数,将其中的内容视为在书写时就存在于程序中这个位置的代码,我们可以根据eval的语意理解为它是通过欺骗,假装成在词法期就存在于当前代码位置来运作的

function foo(str, a) {
  eval(str);
  console.log(a, b)
}
const b = 2
foo('let b = 3', 1);  // 1, 3

但是在严格模式中,eval(...)在运行时有自己的词法作用域,所以在其中的声明无法修改所在的作用域。如下所示:

function foo(str) {
  'use strict';
   eval(str);  //  ReferenceError: a is not defined
   console.log(a,)
}
foo('let a = 2')

new Function(...) 函数的行为也很类似,它最后一个参数可以接收代码字符串,并将其转化为动态生成的函数。这种构建函数的语法比eval(...)略微安全一些。所以同样我们使用new Function(...) 函数也能达到我的目的

(new Function('', `return  ${stringBoolean}`)) ()  // true

为什么不推荐使用eval

eval(...)除了在严格模式下会被影响外,最主要的就是会影响性能。

JavaScript引擎会在编译阶段进行数项性能优化。其中有些优化依赖于能够根据代码的词法进行静态分析,并且预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标志符。标志符--其实就是我们平时所说的变量。

当引擎在代码中发现eval(...), 它只能简单的假设关于标志符位置的判断都是无效的,因为无法在词法分析阶段明确的知道eval(...)会接收到什么代码。

简而言之eval(...)的副作用就是引擎无法在编译时对作用域查找进行优化,因为引擎只能谨慎的认为这样的优化是无效的,从而导致代码运行变慢。

回到最初的问题

根据eval对性能的影响,所以它很自然的会在项目中被禁止使用,new Function(...) 也同样不被允许;

再三探索,JSON.parse(...)可以实现我想要的效果;

好吧,既然不推荐使用,那为什么要这么大幅篇章的介绍呢? 因为好不好使先不管,探索精神不能少呀。就像我们妹子买衣服,穿不穿先不管,购物的体验不能少,哈哈哈哈!

欢迎指正