我有一个像这样的脚本:
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()
中的评估,而不是在新上下文中运行,这与我的我更适合需求。
实际上确实存在解决方案,但是完整性和性能之间存在权衡。
-
只需将返回的函数包裹在另一个功能中:
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
此解决方案几乎没有开销,但这不是一个完整的解决方案。它适用于直接在上下文上暴露的已知功能,但在较小的情况下开始分解。例如。使此功能可以使用高阶功能,地图等。需要相当多的工作,我认为甚至不可能涵盖所有异国情调的组合。
-
使用
Proxy
按需包装功能使用
Proxy
,不需要事先知道哪些值在context
上公开,而是可以按需包装功能。这也使处理高阶功能变得更加容易,因为我们可以递归代表返回值&争论。但是,正确实施的努力要多得多,并且具有重大的性能影响。 -
在新上下文中更改
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
之类的对象时。因此,尽可能避免这种情况。