我有一个JSON格式的JavaScript对象需要根据设置输入进行修改(删除foo或添加栏等)。此输入可以具有级联效果(删除foo可能需要首先根据一组规则删除foobar)。如果在级联中途发生了中断,我不希望JavaScript对象有一半的修改,我想回到原始未修改的对象。
我显然可以将对象的副本发送到修改函数,然后如果函数返回错误则不替换原始对象。这个问题是JavaScript对象大约是25兆字节,因此它的副本意味着浏览器的内存使用量会显着增加。所以我想在不创建对象副本的情况下这样做。我可以让初始对象成为函数的输入并递归地修改它,但是如果在一些调用之后出现错误,它可能会在我不想要的时候返回一个修改过的对象。
有没有办法做我想做的事情?跟踪更改并在发生错误后轻松撤消更改或其他内容?感谢。
答案 0 :(得分:3)
可以创建一个事务数组,记录对对象所做的每个更改,并记录有关更改的足够数据,以便可以撤消。这不是一项微不足道的工作。它有点类似于文字处理器或电子表格中的撤消功能。如果您记录正确的信息级别并将其保持在数组中,则应该可以撤消所有更改。这不是火箭科学,但它是一些编写和测试的代码。
正如评论中所提到的,这里提出的更大问题是一个25MB的对象必须由一个函数操作。我个人想知道你是否可以将数据拆分成更小的部分,并且能够使用较小的副本,以便不必编写整个撤销操作。
此外,值得考虑的是,在您修改对象之前,是否可以预先运行足够的操作以提前知道代码是否会失败。例如,我之前使用的代码运行的所有逻辑与修改对象的代码完全相同,但实际上并没有修改对象。这允许您运行整个操作的预飞行,从而在您实际修改对象之前查明是否有任何检查失败。如果写得恰当,您可以使用相同的代码进行实际修改,只需要传入额外的标记。这是否可行取决于修改操作的确切性质,这是您尚未披露的。
这是处理特定对象属性更改的此类代码的示例。这可以扩展到包括数组操作。
工作演示:http://jsfiddle.net/jfriend00/ohqL0p06/
代码:
function TransactionSummary() {
this.transactions = [];
}
TransactionSummary.prototype = {
deleteProperty: function(parent, property) {
this.rememberTransaction(parent, property, "delete", parent[property]);
delete parent[property];
},
modifyProperty: function(parent, property, newVal) {
this.rememberTransaction(parent, property, "modify", parent[property]);
parent[property] = newVal;
},
addProperty: function(parent, property, newVal) {
this.rememberTransaction(parent, property, "add");
parent[property] = newVal;
},
rememberTransaction: function(parent, property, type, oldVal) {
this.transactions.push({parent: parent, property: property, type: type, oldVal: oldVal});
},
undoTransactions: function() {
var t;
while (this.transactions.length) {
t = this.transactions.pop();
switch(t.type) {
case "delete":
case "modify":
t.parent[t.property] = t.oldVal;
break;
case "add":
delete parent[property];
break;
}
}
}
}