设计模式以检查JavaScript对象是否已更改

Ro.*_*Ro. 8 javascript design-patterns

我从服务器获得了一个对象列表

[{name:'test01', age:10},{name:'test02', age:20},{name:'test03', age:30}]
Run Code Online (Sandbox Code Playgroud)

我将它们加载到html控件中供用户编辑.然后有一个按钮可以将整个列表批量保存回数据库.

我只想发送已更改的对象子集,而不是发送整个列表.

它可以是数组中的任意数量的项目.我想做类似于像Angular这样的框架,它在没有对它进行任何更改时标记像"pristine"这样的对象属性.然后使用该标志仅向服务器发布不是"原始"的项目,即已修改的项目.

Tim*_*ter 12

下面是一个函数,当提供旧的对象数组和一个新的对象数组时,它将返回一个已更改对象的数组:

getChanges = function(oldArray, newArray) {
var changes, i, item, j, len;
if (JSON.stringify(oldArray) === JSON.stringify(newArray)) {
  return false;
}
changes = [];
for (i = j = 0, len = newArray.length; j < len; i = ++j) {
  item = newArray[i];
  if (JSON.stringify(item) !== JSON.stringify(oldArray[i])) {
    changes.push(item);
  }
}
return changes;
};
Run Code Online (Sandbox Code Playgroud)

例如:

var older = [{name:'test01', age:10},{name:'test02', age:20},{name:'test03', age:30}]
var newer = [{name:'test01', age:10},{name:'test02', age:20},{name:'test03', age:20}]
getChanges(older, newer)
Run Code Online (Sandbox Code Playgroud)

(注意test03的年龄现在是20)将返回

[{name:'test03', age:20}]
Run Code Online (Sandbox Code Playgroud)

您只需要导出客户端编辑值的完整列表,将其与旧列表进行比较,然后将更改列表发送到服务器.

希望这可以帮助!


kev*_*628 6

这里有一些想法。

  1. 使用框架。你谈到了 Angular。

  2. 使用Proxy,尽管 Internet Explorer 不支持它。

  3. 可以使用Object.definePropertyset/get 来实现某种更改跟踪,而不是使用经典属性。

  4. 使用吸气剂/设置功能以存储数据,而不是性能:getName()setName()例如。虽然这是defineProperty现在做的旧方式。

  5. 每当您将数据绑定到表单元素时,请设置一个特殊属性以指示该属性是否已更改。类似的东西__hasChanged。如果对象上的任何属性发生更改,则设置为 true。

  6. 老派的暴力方式:保留来自服务器的原始数据列表,将其深度复制到另一个列表中,将表单控件绑定到新列表,然后当用户单击提交时,将原始列表中的对象与新列表中的对象,随时取出更改的对象。可能是最简单的,但不一定是最干净的。

  7. 对 #6 的不同看法:为每个始终返回对象原始版本的对象附加一个特殊属性:

    var myData = [{name: "Larry", age: 47}];
    var dataWithCopyOfSelf = myData.map(function(data) {  
        Object.assign({}, data, { original: data });
    });
    // now bind your form to dataWithCopyOfSelf.
Run Code Online (Sandbox Code Playgroud)

当然,这个解决方案假设了一些事情:(1) 你的对象是扁平和简单的,因为 Object.assign() 不深复制,(2) 你的原始数据集永远不会改变,以及 (3)没有任何内容涉及original.

有很多解决方案。


Rok*_*jan 6

在ES6中我们可以使用Proxy

要完成此任务: 拦截一个对象写入,并将其标记为脏。

代理允许创建一个处理程序对象,它可以捕获、操作并将更改转发到原始目标对象,基本上允许重新配置其行为。我们将采用处理程序set()来拦截对象写入
陷阱。

此时我们可以添加一个不可枚举的属性 标志,例如:_isDirty使用Object.defineProperty()将我们的对象标记为已修改、

当使用陷阱(在我们的例子中是处理程序set())时,不会应用任何更改,也不会反映到对象,因此我们需要使用Reflect.set()将参数值转发到目标对象。

最后,为了检索修改后的对象,filter()使用我们的代理对象的数组来搜索具有自己的 Property 的 "_isDirty"对象。

// From server:
const dataOrg = [
  {id:1, name:'a', age:10},
  {id:2, name:'b', age:20},
  {id:3, name:'c', age:30}
];

// Mirror data from server to observable Proxies:
const data = dataOrg.map(ob => new Proxy(ob, {
  set() {
    Object.defineProperty(ob, "_isDirty", {value: true}); // Flag
    return Reflect.set(...arguments); // Forward trapped args to ob
  }
}));

// From now on, use proxied data. Let's change some values:
data[0].name = "Lorem";
data[0].age = 42;
data[2].age = 31;

// Collect modified data
const dataMod = data.filter(ob => ob.hasOwnProperty("_isDirty"));

// Test what we're about to send back to server:
console.log(JSON.stringify(dataMod, null, 2));
Run Code Online (Sandbox Code Playgroud)

不使用.defineProperty()

如果由于某种原因您不愿意利用原始对象添加额外的属性作为标志,您可以立即使用dataMod引用填充(具有修改对象的数组):

const dataOrg = [
  {id:1, name:'a', age:10},
  {id:2, name:'b', age:20},
  {id:3, name:'c', age:30}
];

// Prepare array to hold references to the modified Objects
const dataMod = [];

const data = dataOrg.map(ob => new Proxy(ob, {
  set() {
    if (dataMod.indexOf(ob) < 0) dataMod.push(ob); // Push reference
    return Reflect.set(...arguments); 
  }
}));

data[0].name = "Lorem";
data[0].age = 42;
data[2].age = 31;

console.log(JSON.stringify(dataMod, null, 2));
Run Code Online (Sandbox Code Playgroud)