我有一个类似这样的脚本:
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
}
但这也没有解决我的问题。
也许使用var someFunc = new Function(arg, body)
会起作用(尚未对其进行测试),但是我无法完全控制传递给vm.runInNewContext
的代码,因此我也无法使用该解决方案。
如何让context.someFunc instanceof Function
在上下文之外返回true?
答案 0 :(得分:1)
这是不可能的。使用相同的上下文无法控制第三方模块。当我遇到类似问题时,首先我尝试修复第三方模块并提交拉取请求并解释错误,然后我放弃并切换到new Function()
中的eval而不是运行新环境,更符合我的需求。
答案 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
此解决方案(几乎)没有开销,但它不是一个完整的解决方案。它适用于直接在上下文中公开的已知函数,但它在不太重要的情况下开始崩溃。例如。让它与高阶函数,地图等一起工作需要相当多的工作,我认为甚至不可能涵盖所有奇特的组合。
使用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
这样的对象时。所以只要有可能,就应该避免这种情况。