前言
最近在项目开发中,有了这么个需求,数据返回的是字符串类型的表达式,需要根据该表达式生成对应的分数。我的项目是vue2+ts,所有一开始我直接在模板中写道:
<tempalte>
// 用的是elemntUI的table组件,反正下面的scope.row.formula能拿到需要的字符串
<div>{{ scope.row.formula }}</div>
</template>
可是很快就出现问题了,它并不会运行这个字符串,而是直接返回了它,所以我们就需要能够运行字符串的方法了,开百。
eval
首先,eval可以实现我们需要的效果:
// 伪代码
<tempalte>
// formula的内容是:'a*b'
<div>{{ getScore(scope.row.formula) }}</div>
</template>
getScore(value: string) {
const str = value
const a = 1
const b = 2
const res = eval(str) // 2
}
但是使用eval有诸多不足:
首先,是性能很差,我们都知道,js是存在一个预编译机制,也就是说在代码实际执行前就已经确定了代码的执行位置和顺序,这样能大大提升性能,可是当js执行到eval()方法时,由于不确定它其中会被传入什么代码,所以不可以对其进行预编译,也就导致了其性能的低下;
其次,由于eval会执行所有传入其中的js代码,而eval是在我们的项目中的,那也就意味着,我们无法控制eval中代码的来源,而外部代码可以通过eval()执行来获取系统中的信息,这是不安全的。
new Function
我们还可以选择别的方法来代替eval,比如利用new Function()来执行字符串,示例如下:
const str = 'return 1*2'
const fun = new Function(str)
const res = fun() // 2
但是我们需要传入变量,那么就得改变一下,有几种方式可以传值,在此列出两种:
-
直接在new Function()中传入对应的变量
const str = 'return a * b' const fun = new Function('a', 'b', str) const res = fun(1, 3) // 3
-
传入一个参数集合,通过apply、call、bind等可以改变this指向的方法获取参数
// 由于new Function()中的作用域是全局的,所以如果要使用指定对象的this,需要手动调整this指向 const str = 'return this.a*this.b' const params = { a: 1, b: 4 } const fun = new Function(str).bind(params) const res = fun()
OK!
注意:
(new function()实际上也存在安全问题,原理和eval一样,但是也不用过于担心,只要我们传入的js代码能够保证不涉及保密内容,或者干脆就是我们自己定义的数据,那么就不需要有这方面的担心