如何正确管理对象的多级代理

时间:2016-10-19 22:56:55

标签: javascript typescript

this SO question所示,Proxy个对象是观察对象变化的好方法。

如果您想观看子对象的更改,该怎么办?您还需要代理这些子对象。

我正在使用一些自动执行此操作的代码 - 只要您尝试设置属性,就会确保它包含在代理中。我们这样做是为了让我们可以在每次任何值变化时执行操作。

function createProxiedObject(objToProxy) {
  return new Proxy(objToProxy, {
    set: function (target, key, value) {
    //proxy nested objects
    if (value !== null && typeof value === 'object') {
      value = createProxiedObject(value);
    }
    target[key.toString()] = value;

    handleProxiedObjectChange();
  });

这很有效,但至少有一种情况可以解决这个问题:

function ensureObjectHasProperty(object, key, default) {
  if (object[key] !== null) {
    // some validation happens here
    return object[key];
  } else {
    return default;
  }
}

...

proxiedObject = somethingThatCreatesAProxiedObject(someValue);
proxiedObject[someProperty] = ensureObjectHasProperty(proxiedObject, someProperty, defaultValue)

结果是someProperty(已经被代理)下的值被重新分配给代理对象,导致它被包装在另一个代理中。这导致每次对象的任何部分发生更改时都会多次调用handleProxiedObjectChange方法。

解决这个问题的简单方法是永远不要向代理对象分配任何东西,除非它是新的,但是由于这个问题已经发生,所以将来有人会再次这样做。如何修复set函数以不重新包装已经被代理的对象?或者是否有更好的方法来观察对象,以便在对象或其任何子对象发生更改时可以调用handleProxiedObjectChange

1 个答案:

答案 0 :(得分:1)

正如@DecentDabbler所建议的,使用WeakSet允许我确保我从不尝试将代理包装在另一个代理中:

const proxiedObjs = new WeakSet();

...

function createProxiedObject(objToProxy) {
  // Recursively ensure object is deeply proxied
  for (let x in objToProxy) {
    subObj = objToProxy[x];
    if (subObj !== null && typeof subObj === 'object' && !proxiedObjs.has(subObj)) {
      objToProxy[x] = createProxiedObject(subObj);
    }
  }

  let proxied = new Proxy(objToProxy, {
    set: function (target, key, value) {
      //This check is also new - if nothing actually changes
      //I'd rather not call handleProxiedObjectChange
      if (_.isEqual(target[key.toString()], value)) {
        return true;
      }

      //proxy nested objects
      if (value !== null && typeof value === 'object' && !proxiedObjs.has(value)) {
        value = createProxiedObject(value);
      }
      target[key.toString()] = value;

      handleProxiedObjectChange();
  });
  proxiedObjs.add(proxied);
  return proxied;