NodeJS: runInNewContext and instanceof



我有一个像这样的脚本:

var context = {}
vm.runInNewContext("var someFunc = function() {}", context);
console.log(typeof context.someFunc); //function
console.log(context.someFunc instanceof Function); //false

我确实理解了为什么第四行返回false:在新上下文中有一个新的Function对象,该对象在外部上下文中不等于Function对象。因此,context.someFunc不是该外部Function对象的实例。

但是,使用instanceof Function的第三方库使用了context.someFunc功能。由于context.someFunc是一个函数,但在此上下文中不是Function的实例,因此第三方不会在应有的函数时将其视为函数,因此它会崩溃。我尝试使用以下上下文:

var context = {
    "Function" : Function
}

但这也不能解决我的问题。

也许使用var someFunc = new Function(arg, body)可以工作(尚未测试),但是我没有完全控制传递给vm.runInNewContext的代码,因此我也无法使用该解决方案。

如何让context.someFunc instanceof Function在上下文之外返回真实?

这是不可能的。使用相同的上下文您无法控制第三方模块。当我遇到类似问题时,首先,我尝试修复第三方模块并提交拉动请求,并说明出了什么问题,然后我放弃并切换到 new Function()中的评估,而不是在新上下文中运行,这与我的我更适合需求。

实际上确实存在解决方案,但是完整性和性能之间存在权衡。

  1. 只需将返回的函数包裹在另一个功能中:

     const context = {}
     vm.runInNewContext("var someFunc = function() {}", context);
     const someFunc = context.someFunc;
     context.someFunc = function(...args) { return someFunc.apply(this, args); }
     console.log(context.someFunc instanceof Function); //true
    

    此解决方案几乎没有开销,但这不是一个完整的解决方案。它适用于直接在上下文上暴露的已知功能,但在较小的情况下开始分解。例如。使此功能可以使用高阶功能,地图等。需要相当多的工作,我认为甚至不可能涵盖所有异国情调的组合。

  2. 使用Proxy按需包装功能

    使用Proxy,不需要事先知道哪些值在context上公开,而是可以按需包装功能。这也使处理高阶功能变得更加容易,因为我们可以递归代表返回值&争论。但是,正确实施的努力要多得多,并且具有重大的性能影响。

  3. 在新上下文中更改Function原型

    const context = { OuterFunction : Function};
    vm.runInNewContext(`
      // setup
      Object.setPrototypeOf(Function.prototype, OuterFunction.prototype);
      //script
      const someFunc = function() {};
    `, context);
    console.log(context.someFunc instanceof Function);
    

    此解决方案使内部Function扩展了外部Function,因此任何内部函数都是外部函数的实例。但是,使用setPrototypeOf对性能非常不好,尤其是在更改诸如Function之类的对象时。因此,尽可能避免这种情况。

最新更新