4

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.

Michael M.
  • 10,486
  • 9
  • 18
  • 34
Seddiki Anass
  • 306
  • 2
  • 19
  • You might be able to if you wrap it in an additional object and listen on that. – ankr Jun 29 '17 at 12:12
  • Have you tried using the actual debugger API of your js engine? – Bergi Jun 29 '17 at 14:11
  • What do you mean by "non destructive"? Even your proxy solution requires changing some of the code to wrap a specific object. – Bergi Jun 29 '17 at 14:12
  • @Bergi I have tried using Chrome debugger but it doesn't warn me when the object gets changed. That's why I had to imagine such a solution. By non destructive I meant that all further use of the object to watch will succeed and perform the same operation(s) as the non wrapped object. My proxy is non destructive because I am still able to use the modified object as before, the proxy only added a notification of changes by calling `debugger;`. – Seddiki Anass Jun 29 '17 at 14:41
  • @SeddikiAnass You at least use it to [watch variables](https://stackoverflow.com/questions/11618278/how-to-break-on-property-change-in-chrome) which is exactly what you are after. Regarding watching all object properties, see [this question](https://stackoverflow.com/questions/11618278/how-to-break-on-property-change-in-chrome) – Bergi Jun 29 '17 at 15:07
  • @Bergi This is exactly the code I used as a base (with some extensions) to create my watcher, I'll edit my question to add the link to this post. But now I'd like to detect when the address on which the variable points changes. I don't know if it's possible. – Seddiki Anass Jun 29 '17 at 15:23

1 Answers1

2

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

Joseph Barnes
  • 620
  • 5
  • 10
  • Hello Joseph, I did not think of such a solution. This looks good and I'll test it. Thank you :) – Seddiki Anass Jun 30 '17 at 15:13
  • However, I just have two questions : What's the purpose of _privateObject ? and What is the difference between 'name' and 'val' arguments ? – Seddiki Anass Jun 30 '17 at 15:27
  • _privateObject stores a private variable in the function's name space that can only be changed via a routine decleared within the function. We need this in javascript because getters and setters cannot be defined in a property of a variable with the same name. (var a cannot be created) – Joseph Barnes Jun 30 '17 at 15:48
  • Name is the properties name on the namespace. Val is the object we want to initially store. – Joseph Barnes Jun 30 '17 at 15:55
  • It works great, I still need to tweak it as it seems not to accept global objects (window.foo ect...) but it's not impacting my case. Thank you for your help – Seddiki Anass Jun 30 '17 at 16:25