0

我有一个执行以下操作的脚本:

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

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

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

var context = {
    "Function" : Function
}

但这也没有解决我的问题。

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

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

4

2 回答 2

1

这是不可能的。在您无法控制第三方模块的情况下使用相同的上下文。当我遇到类似的问题时,首先我尝试修复第三方模块并提交拉取请求并解释问题所在,然后我放弃并切换到 eval innew Function()而不是在新的上下文中运行,这更符合我的需求.

于 2012-11-07T19:08:46.910 回答
1

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

  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. 使用 aProxy按需包装函数

    使用 a 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. 因此,只要有可能,就应该避免这种情况。

于 2017-10-27T07:51:24.053 回答