克隆整个JavaScript ScriptEngine

Rad*_*scu 6 javascript java clone rhino scriptengine

我需要以某种方式深度克隆我的ScriptEngine对象的整个绑定集.

我试过了什么

  • 到目前为止,我已经尝试过Cloner库来克隆整个Bindings结构.如果它有用,那将是很好的,因为它可以确保精确的副本,包括私有变量.但这会导致jvm堆损坏(jvm只是崩溃,退出代码为-1073740940).有时它不会崩溃,但会发生奇怪的事情,比如System.out.println()停止工作应该......

  • 我还研究了使用ScriptEngine中的js代码克隆对象,这样我就可以将它们作为NativeObjects获取并在一些java地图中管理它们.但是我发现的所有克隆方法都存在缺陷.我想要一个精确的对象快照.例如,如果每两个对象A和B的包含字段(比如a.fa和b.fb),其引用相同的对象C,使用克隆时jQuery.extend()(例如)的字段a.fab.fb克隆的a和b的将引用的不同的克隆c,而不是引用一个相同的克隆.还有许多其他边缘问题.

  • 我还尝试使用Cloner克隆整个ScriptEngine(不仅仅是绑定),我还尝试使用Rhino的js引擎并克隆整个范围(而不是Bundeled ScriptEngine包装器).但堆腐败问题仍然存在.

为什么我需要这样做

我需要这个,因为我必须能够将整个ScriptEngine绑定的值恢复到之前的某个点.我需要制作绑定的精确快照.

该应用程序是我的博士研究项目的一部分,该项目包括运行状态机和节点(在java中实现),其中附带了js代码.js代码由最终用户输入,并且在运行时被逐出.当无法通过路径到达最终状态时,算法会向后退步,尝试查找备用路径.在每个步骤向后,它必须撤消js引擎绑定中可能发生的任何更改.


所有全局变量名称在js evaling之前都是已知的,并且是对象(用户在节点的代码中键入,然后将其组织(在java中)到具有某些名称模式的js对象中).但是他们的内容可以是任何东西,因为它是由用户js代码控制的.

所以我想我现在唯一的解决方法是使用js代码克隆js对象.

小智 3

除了“边缘情况”之外,jQuery.extend 还可以按照您提到的方式使用。a b并且它们的克隆都将引用同一个对象c

var c = { f:'see' };
var a = { fa: c };
var b = { fb: c };
var cloneA = $.extend({}, a);
var cloneB = $.extend({}, b);
console.log(a.fa === b.fb, cloneA.fa === cloneB.fb, a.fa === cloneB.fb);
// true true true
Run Code Online (Sandbox Code Playgroud)

但似乎您想要克隆所有对象(包括c),同时跟踪对象的关系。为此,最好使用对象关系表。

我在嵌套的 javascript 对象和 JSON 中经常看到这种情况,因为人们往往会忘记 JSON 纯粹是一种文本格式。除了单个文本字符串之外,JSON 文件中没有实际的 javascript 对象instanceof String。javascript 中没有豆类、泡菜或任何富含防腐剂的食物。

在对象关系表中,每个“表”只是一个“平面”对象数组,仅具有原始值属性和指向表(或另一个表)中其他对象的指针(而不是引用)。指针可以只是目标对象的索引。

因此,上述对象关系的 JSON 版本可能类似于

{
    "table-1":[
        { "a": { "fa":["table-2",0] } },
        { "b": { "fb":["table-2",0] } }
    ],
    "table-2":[
        { "c": { "name":"see" } },
        { "d": { "name":"dee" } },
        { "e": { "name":"eh.."} }
    ]
}
Run Code Online (Sandbox Code Playgroud)

解析器可能看起来像

var tables = JSON.parse(jsonString);
for(var key in tables){
    var table = tables[key];
    for(var i = 0; i < table.length; i++){
        var name = Object.keys(table[i])
        var obj = table[i][name];
        for(var key2 in obj){
            if(obj[key2] instanceof Array && obj[key2][0] in tables){
                var refTable = obj[key2][0];
                var refID    = obj[key2][1];
                var refObj   = tables[refTable][refID];
                var refKey   = Object.keys(refObj)[0];
                obj[key2] = refObj[refKey];
            }
        }
        this[name] = obj;
    }
}

console.log(a.fa === b.fb, b.fb === c);
// true true
Run Code Online (Sandbox Code Playgroud)

我意识到对象关系映射有其缺点,但是拍摄脚本引擎的快照听起来确实有点疯狂。特别是因为您的目的是能够回忆起之前的每个步骤,因为这样您的每个步骤都需要一个新的快照...这将很快占用大量磁盘空间..除非您只是跟踪快照每个步骤之间都有差异,就像 git 存储库一样。实现看似简单的“撤消”方法听起来需要大量工作。

该死..想想看,为什么不将每个步骤存储在历史文件中呢?然后,如果您需要后退一步,只需截断上一步中的历史文件,然后在新环境中重新运行每个步骤即可。

不确定使用 java 有多实用(性能方面)。Nodejs(就目前而言)比任何 Java 脚本引擎都要快。事实上,从现在开始我就将其称为 ECMAscript。抱歉,我的咆哮只是这样

Java 很慢,但你已经知道了。
因为它很容易表明,速度很快。

  • 感谢您的详细答复。正如您所说,我最终使用 js 克隆对象并在 js 中维护克隆历史记录。我的回答中详细描述了克隆方法http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object/11462081#11462081。当克隆一个新对象时,会将唯一的 ID 添加到原始对象中,并将克隆对象添加到地图中。当必须再次克隆同一对象时,将使用映射中的克隆,因为原始对象具有唯一的 ID。最终项目是http://sourceforge.net/projects/ggs/ (2认同)