Transform a Javascript object into a proxy (and not its reference)

不问归期 提交于 2020-01-24 20:25:06

问题


I can take a Javascript object o and create a new Proxy object from it:

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

But is there a way to mutate an existing object reference to track changes on the original object? In particular, is there a way I can track the addition of new keys on the object from exterior sources?


回答1:


The Proxy spec supports defining a proxy on the prototype of an object as a means for inspecting actions on that object when they do not exist on the instance. While this isn't full parity with .watch() it does allow for your mentioned use case of knowing when new properties are added. Here is an example, with comments about caveats...

  // 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



回答2:


Just create the object first and keep a reference to it before creating its Proxy.

Now you can modify either of them (the original object or its Proxy) and the other will also receive the changes unless you prevent them on the Proxy:

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);

If you want to be notified when a new property is added, deleted or modified on an object without the possiblity that the original reference is used to mutate the original object directly, the only option you have is to create that object directly as a Proxy or overwrite the original one:

// 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;

There used to be an Object.prototype.watch(), but it has been deprecated.



来源:https://stackoverflow.com/questions/52031628/transform-a-javascript-object-into-a-proxy-and-not-its-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!