js字符串转函数

js字符串转函数

对于函数型的字符串,我们想要将其转换为一个函数然后执行,有2种常用的方法。

1.eval()

eval()具有可以解析表达式的特性,所以可以利用这一特性将字符串解析为一个函数。

  let funcStr = "function test(value){alert(value)}";
  let test = eval("(false || "+funcStr+")");
  test("函数能够执行");

这里eval实际是解析了表达式(false || function test(value))

但是需要注意的事eval可以解析任何字符串,这是不安全的,请尽量不要使用。

2.new Function()

new Function ([arg1[, arg2[, ...argN]],] functionBody)

new Function()只接受字符串参数,其可选参数为方法的入参,必填参数为方法体内容。

function add(a, b) {
  retrun a + b;
}
//等价于
var add = new Function ('a', 'b', 'return a + b');

我们可以利用它最后的方法体参数,直接返回一个函数。那么执行这个新的创建函数后得到的就是我们需要的函数。

let funcStr = "function test(value){alert(value)}";
let funcTest = new Function('return '+funcStr);
funcTest()("函数也能够执行")

3.深入理解

如果是一个完整的函数体,且其中函数体内未包含其他函数,那么可以使用new Functioneval来实现。

如果函数体内包含函数,那么包含的函数必须和被调用的函数位于同一作用域下。

如何保证调用函数和调用参数在统一作用域?有2种方案可以采取:

  • 将变量和方法都声明到window对象上,那么任何地方皆可以调用,但是这样存在风险,如果存在和window对象内置属性同名的情况下,将覆盖内置属性的值。
  • 将变量和方法作用域一个实例对象中。

下面讲解如何用实例限制作用域。

  1. 首先创建一个构造方法,构造方法中声明变量和方法,如果存在动态变量时,可如下操作,将变量放在this对象上,这样在实例中即可通过this.属性名获取。
  2. 定义一个方法,调用字符串函数。
  3. 在方法内绑定this指向,并使用with省略表达式的前缀,例如this._f即可省略为_f;

let exp = _f("defaultStr")(_f("DFormat")(reportDate,'YYYY-MM-DD'))字符串函数为例。

其中_f为一个函数,reportDate为一个变量

如果直接使用eval(exp),会报错_f is not defined,因为默认情况下在严格模式下_f的作用域为undefined。

此时你可以设置window为作用域,例如:

//如果只是单纯的一个函数_f("DFormat")(reportDate,'YYYY-MM-DD')
//那么可与如下设置:
window._f = resolveFilter
expStr = `_f("DFormat")(reportDate,'YYYY-MM-DD')`
window.reportDate = "20191212";
let a = eval(expStr)
console.log(a)

将变量和方法绑定到window上,然后进行全局调用

但是上例是2个方法_f("defaultStr")(_f("DFormat")(reportDate,'YYYY-MM-DD')),函数调用时,立即执行

_f("DFormat")(reportDate,'YYYY-MM-DD'),返回的结果不是一个函数,导致调用_f("defaultStr")时,_f方法被第二个函数的执行结果覆盖了,不再是一个方法了,所以不能继续执行。

要解决方法覆盖问题,那么方法就不能被定义为属性,而应该是原型,利用继承的特性解决该问题。

实例解决方案

//注册filter
export const resolveFilter = (id) => {
    if (typeof id !== 'string') {
        return id;
    } else {
        return filters[id]//返回一个函数
    }
}

//创建实例对象
export function createExpInstance(paramsEntity) {
    for (let key in paramsEntity) {
        this[key] = paramsEntity[key]
    }
}
createExpInstance.prototype._f = resolveFilter;
createExpInstance.prototype.getValue = function (exp) {
    let code = `return (()=>{with (this) {return ${exp}}})()`;
    try {
        let func = new Function(code);
        let currentFunc = func.bind(this);//绑定函数this指向,严格模式下this为undefined,非严格模式为window
        return currentFunc();
    } catch (err) {
        // console.log(err)
    }
}

let expInstance = new createExpInstance(paramsEntity);
let expValue = expInstance.getValue(expStr);

with(this)虽然是个不推荐属性,但是用在这里恰到好处,让我们省略了this的书写,从而直接利用实例属性和方法。

如果没有with(this),code应为:

return (()=>{return this._f("defaultStr")(this._f("DFormat")(this.reportDate,'YYYY-MM-DD'))})()
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值