javascriptmutationes6-proxy

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


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?


Solution

  • 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