Search code examples
javascriptjavascript-objectses6-proxy

Detect when the reference of a variable changes


I am currently writing a tool that monitors changes made to an object using a Proxy. So I have a function watchObject that takes an object as the argument and wraps it inside a Proxy where the handlers corresponding to changes in the object call debugger;. This watchObject is mostly based on the accepted answer of this question.

Setting traps on get, defineProperty and deleteProperty handlers works quite well when the object is modified only.
However when the reference is replaced the handlers are not called and the Proxy wrapping around it is lost.

Lets consider an Object a containing a key foo:
var a = { foo: "bar"};

For example the following will invoke a debugger breakpoint that are inside my traps:

  • a.foo = "Hello"
  • delete a.foo
  • a.baz = "Hi" ect...

But calling this afterward: a = {keyOne: "one"} will not trigger the breakpoint and will subsequent calls to above examples (that otherwise would trigger the breakpoint) will not invoke a breakpoint anymore.

So I would like to know if there is a way to detect an operation like: a = {keyOne: "one"} is done so as to monitor reference changes for a variable and be able to recreate the proxy object on the new referenced object.

Also, as the whole process of monitoring changes on an object is aimed to make debugging easier, the solution has to be non destructive on the code that is involved.
Using a Proxy is great as it only intercepts and doesn't change overall behaviour of the object wrapped.


Solution

  • I want to give this a shot...

    It looks like you want to catch the object itself instead of the method.

    Since the object's variable will be set as a property of the window or another object we can use a function to define a getter and setter on the window (or that object) with the desired variable name:

    function trackedProxy(name, val, _this){
        let handler = {} // place your property traps here
        let _privateObject = val
        let _privateProxy = new Proxy(_privateObject, handler)
    
        Object.defineProperty(_this, name, {
        get: function() {
          return _privateProxy;
        },
        set: function(value) {
            console.log("object changed")
            // Do something
            return _privateObject = value;
        }
      });
    }
    
    //build one with invocation of the function, but do not set as equal to a var or you will have the same issue. 
    //**bad! - var a = trackedProxy('a',{keyOne: "one"},this)
    trackedProxy('a',{ foo: "bar"}, this)
    
    console.log(a)
    //Proxy{ foo: "bar"}
    
    a={keyOne: "one"}
    //Object changed
    
    console.log(a)
    //Proxy{keyOne: "one"}
    

    Keep in mind that you cannot redefine the property on the window after you do this.

    I hope this helps :)