将Javascript对象转换为代理(而不是其引用)

时间:2018-08-27 01:41:55

标签: javascript proxy mutation

我可以获取一个Javascript对象o并从中创建一个新的Proxy对象:

let p = new Proxy(object, { ... })

但是有一种方法可以改变现有对象的引用以跟踪原始对象的变化?特别是,有什么方法可以跟踪外部源在对象上添加的新键吗?

2 个答案:

答案 0 :(得分:2)

只需先创建对象,并在创建其代理之前对其进行引用即可。

现在,您可以修改其中任何一个(原始对象或其代理),并且除非您在代理上阻止它们,否则另一个也将收到更改:

const o = {};
const p = new Proxy(o, {
  set: function(obj, prop, value) {
    if (prop === 'd') {
      return false;
    }
    
    obj[prop] = value;
    
    return true;
  },
});

// These operations are forwarded to the target object o:
p.a = 0;
p.b = 1;

// This one is prevented by the Proxy:
p.d = true;

// Both will have two properties, a and b:
console.log(o);

// You can also mutate the original object o and the Proxy will also get those changes:
o.c = false;

// Note that now the Proxy setter is not called, so you can do:
o.d = true;

// But the Proxy still gets the change:
console.log(p);

如果要在对象上添加,删除或修改新属性时收到通知,而又没有使用原始引用直接使原始对象发生变异的可能性,那么唯一的选择就是直接创建该对象为代理或覆盖原始代理:

// Created from an empty object without a reference to it:
// const p = new Proxy({}, { ... });

// Overwrite the original reference:
let myObject = { a: 1, b: 2 };

myObject = new Proxy(myObject, {
  set: function(obj, prop, value) {
    if (prop in obj) {
      console.log(`Property ${ prop } updated: ${ value }`);
    } else {
      console.log(`Property ${ prop } created: ${ value }`);
    }

    obj[prop] = value;

    return true;
  },
  
  deleteProperty(obj, prop) {
    console.log(`Property ${ prop } deleted`);
  
    delete obj[prop];
  }
});

// Now there's no way to access the original object we
// passed in as the Proxy's target!

myObject.a = true;
myObject.a = false;
delete myObject.a;

曾经有一个Object.prototype.watch(),但已经过时了。

答案 1 :(得分:1)

代理规范支持在对象的原型上定义代理,以作为检查实例上不存在对该对象的操作的一种手段。尽管这与.watch()并不完全相同,但确实允许您提到的用例知道何时添加新属性。这是一个示例,其中包含有关警告的评论...

  // assuming some existing property you didn't create...
  const t = { existing: true };

  // proxy a new prototype for that object...
  const ctr = {};
  Object.setPrototypeOf(t, new Proxy(ctr, {
    get(target, key) {
      console.log('icu get');
      return Reflect.get(target, key) || ctr[key];
    },
    set(target, key, val) {
      console.log('icu set');
      // setting this container object instead of t keeps t clean, 
      // and allows get access to that property to continue being 
      // intercepted by the proxy
      Reflect.set(ctr, key, val);
      return true;
    },
    deleteProperty(target, key) {
      console.log('icu delete');
      delete ctr[key];
      return true;
    }
  }));

  // existing properties don't work
  console.log('existing');
  t.existing; // <nothing>
  t.existing = false; // <nothing>

  // new properties work
  console.log('new');
  t.test; // icu get
  t.test = 4; // icu set
  console.log(t.test); // icu get
                       // 4

  // but this doesn't work (and I think it should be a bug)
  console.log('delete');
  delete t.test; // icu get
                 // <missing icu delete>
  console.log(t.test); // 4