在Nashorn中跨上下文共享本机JavaScript对象

时间:2015-12-08 02:53:24

标签: javascript java nashorn

我有一个Nashorn引擎,我在其中评估了一些暴露一些常用实用程序功能和对象的脚本。我希望自定义脚本在自己的上下文中运行而不是相互跳过,所以我使用engine.createBindings()为它们创建新的上下文:

ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
newContext.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));

现在我可以访问在原始范围中创建的所有内容,但这也为新上下文创建了一个全新的全局对象,这意味着本地JS对象的实例,如Object,{{ 1}}等与原始上下文中的对应实例不同。

这会导致一些奇怪的行为。例如,假设您具有在引擎中评估的以下代码(即“父”上下文“):

Number

现在假设你的自定义脚本如下:

function foo(obj) {
    print(JSON.stringify(obj, null, 4));
    print(Object.getPrototypeOf(obj) === Object.prototype);
}

我对function bar() { foo({a: 10, b: 20}); } 进行评估,然后调用函数:

newContext

返回:

engine.eval(source, newContext);
ScriptObjectMirror foo = newContext.getAttribute("foo", ScriptContext.ENGINE_SCOPE);
foo.call(null);

这是expected behavior,因为在其他上下文中创建的对象被视为外来对象。

我要做的是公开一个公共函数库,并在一个脚本引擎实例中维护它。我不想继续重新创建脚本引擎实例,因为我最终失去了JIT优化(我在某处读到了这个,但我现在找不到链接)。我确实喜欢这样一个事实:对象“记住”它们的原始全局上下文,但我希望在原生JS对象的情况下不会发生这种情况。

有没有办法创建一个全新的全局上下文,同时仍然共享JS全局对象实例?我尝试手动复制这些实例(枚举undefined false 的属性),但是当我将它们复制到新的上下文时,它们是this个实例而不是未解包的版本。我认为这是因为它们最初是在不同的环境中创建的,因此被认为是“外来的”。

1 个答案:

答案 0 :(得分:1)

看起来不幸的是不可能这样做。我甚至打开了对象并取回了本机对象(我是从主线程中完成的),然后覆盖了新环境中的对象。遗憾的是,由于Java中表示本机对象的Java类维护对创建的原始实例(对于对象的原型)的内部引用,因此仍然无效。

上述两种情况的解决方法是为对象测试执行此操作:

var proto = Object.getPrototypeOf(object);
return (typeof proto.constructor !== "undefined" && (proto.constructor.name === Object.prototype.constructor.name));

对于JSON,我通过评估主上下文中的Douglas Crockford's JSON library和每个新上下文来覆盖原生JSON对象。我确实需要进行一些修改才能让它在Nashorn中运行。第一个是让它重新定义JSON对象而不首先检查它的存在(所以只需删除if测试)。第二个是在hasOwnProperty内使用对象本身的stringify。所以以下一行:

if (Object.prototype.hasOwnProperty.call(value, k))

更改为:

if (Object.prototype.hasOwnProperty.call(value, k) || (value.hasOwnProperty && value.hasOwnProperty(k)))

通过这两项改变,我能够让事情发挥作用。