我需要以某种方式深度克隆我的ScriptEngine对象的整个绑定集。
到目前为止,我已尝试Cloner library来克隆整个Bindings结构。如果它有用,那将是很好的,因为它可以确保精确的副本,包括私有变量。但这会导致jvm堆损坏(jvm只是崩溃,退出代码为-1073740940)。有时它不会崩溃但会发生奇怪的事情,比如System.out.println()
停止工作......
我还研究了在ScriptEngine中使用js代码克隆对象,这样我就可以将它们作为NativeObjects来获取并在一些java地图中管理它们。但是我发现的所有克隆方法都存在缺陷。我想要一个精确的对象快照。例如,如果两个对象a和b中的每一个都包含引用相同对象c的字段(例如a.fa和b.fb),则使用jQuery.extend()
(例如)克隆字段a.fa
和{克隆的a和b的{1}}将引用c的不同克隆,而不是引用同一个克隆。还有许多其他边缘问题。
我还尝试使用Cloner克隆整个ScriptEngine(不仅仅是绑定),我还尝试使用Rhino的js引擎并克隆整个范围(而不是Bundeled ScriptEngine包装器)。但堆腐败问题仍然存在。
我需要这个,因为我必须能够将整个ScriptEngine绑定的值恢复到之前的某个点。我需要对绑定进行精确的快照。
该应用程序是我的博士研究项目的一部分,该项目包括运行状态机和节点(在java中实现),其中附带了js代码。 js代码由最终用户输入,并且在运行时被逐出。当无法通过路径到达最终状态时,算法会向后退步,尝试查找备用路径。在每一步向后,它必须撤消js引擎绑定中可能发生的任何更改。
所有全局变量名称在js evaling之前都是已知的,并且是对象(用户在节点的代码中键入,然后将其组织(在java中)到具有特定名称模式的js对象中)。但它们的内容可以是任何内容,因为它由用户js代码控制。
所以我想我现在唯一的解决办法是使用js代码克隆js对象。
答案 0 :(得分: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
但似乎你想要克隆所有对象(包括c
),同时跟踪对象'关系。为此,最好使用对象关系表。
我通过嵌套的javascript对象和JSON看到了很多,因为人们往往会忘记JSON纯粹是一种文本格式。除了单个文本字符串instanceof String
之外,JSON文件中没有实际的javascript对象。 javascript中没有豆类或泡菜或任何防腐剂重的食物。
在对象关系表中,每个表'只是一个" flat"只有原始值属性的对象和指向表(或另一个表)中其他对象的指针(不是引用)。指针可以只是目标对象的索引。
因此上述对象关系的JSON版本可能类似于
{
"table-1":[
{ "a": { "fa":["table-2",0] } },
{ "b": { "fb":["table-2",0] } }
],
"table-2":[
{ "c": { "name":"see" } },
{ "d": { "name":"dee" } },
{ "e": { "name":"eh.."} }
]
}
解析器可能看起来像
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
我意识到对象关系映射有它的垮台,但拍摄脚本引擎的快照确实听起来有点疯狂。特别是因为你的目的是能够回忆起前面的每一步,因为那时你需要为每一步创建一个新的快照...这将很快占用大量的磁盘空间..除非你只是跟踪快照在每个步骤之间进行区分,就像git repo一样。这听起来像是一项非常艰巨的工作来实现一个看似简单的&#34;撤消&#34;方法
该死的......想一想,为什么不将每一步存储在历史文件中?然后,如果您需要后退,只需截断上一步中的历史记录文件,然后在新环境中再次运行每个步骤。
不确定使用java有多么实用(性能明智)。 Nodejs(现在存在)比任何java脚本引擎都要快。事实上,从现在开始我就把它称为ECMAscript。对不起那个咆哮,只是那个
Java很慢,但你已经知道了 因为它很容易表明,它的速度和它一样快。
答案 1 :(得分:2)
我可以建议采用不同的方法。
不要尝试克隆ScriptEngine。它没有实现Serializable / Externalizable,API也不支持克隆。尝试强制克隆将是未来Java版本可能会破坏的解决方法。
使用cycle.js将绑定序列化为JSON。它将以{$ref: PATH}
形式编码对象引用。反序列化时,它将恢复引用。
据我所知, cycle.js 不会序列化函数,但可以使用Function.toString()自行添加函数序列化(参见下文和Example)
或者,如果使用库不是一个选项,则可以非常直接地实现自己的序列化以满足您的需求:
var jsonString = JSON.stringify(obj, function(key, val) {
if (typeof(value) === 'function')
return val.toString();
// or do something else with value like detect a reference
return val
})