Bootstrap

js中运行字符串中的表达式

前言

最近在项目开发中,有了这么个需求,数据返回的是字符串类型的表达式,需要根据该表达式生成对应的分数。我的项目是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代码能够保证不涉及保密内容,或者干脆就是我们自己定义的数据,那么就不需要有这方面的担心

;