Ryan King
Ryan King

Reputation: 3696

Log what getter gets using proxy

I have the following proxy handler that logs get events.

const proxyHandler = {
  get: function(target, prop) {
    console.log("get", prop);
    return Reflect.get(target, prop);
  }
};

const obj = new Proxy(
  {
    value: 4,
    text: "hi",
    get textVal() {
      return this.text.repeat(this.value);
    },
    getTextVal() {
      return this.text.repeat(this.value);
    }
  },
  proxyHandler
);


console.log("------- normal func -------")
console.log(obj.getTextVal());
console.log("------- getter func -------")
console.log(obj.textVal);

When I log console.log(obj.getTextVal()) I get:

get getTextVal 
get text 
get value 
hihihihi 

But when I log the getter console.log(obj.textVal), I only get the following:

get textVal 
hihihihi 

How can I make obj.textVal log the get text and get get value events using proxy? ie. When running console.log(obj.textVal) I would like the following result.

get getTextVal 
get text 
get value 
hihihihi 

Upvotes: 1

Views: 85

Answers (2)

Bertalan Miklos
Bertalan Miklos

Reputation: 231

The above answer works but there is a bit more elegant solution. You are missing the receiver in your Proxy trap and Reflect arguments. Simply change the Proxy to this:

const proxyHandler = {
  get: function(target, prop, receiver) {
    console.log("get", prop);
    return Reflect.get(target, prop, receiver);
  }
};

Notice the new receiver in the trap and Reflect arguments.

There is an important distinction between a Proxy trap target and receiver. In this case, the target is the underlying raw object while the receiver is the Proxy wrapper. If you do not pass the receiver to the Reflect call everything inside the get operation will be run against the raw object and won't trigger the Proxy traps.

If you have the time I suggest you read the relevant parts of the ES6 spec to fully grasp the difference between these two. Otherwise, just make sure that you forward all Proxy trap args to the matching Reflect call if you are aiming for a transparent wrap.

Upvotes: 2

Marinos An
Marinos An

Reputation: 10808

You can set the Proxy instance to proxyHandler object and access the properties through it (instead of this).

const proxyHandler = {
  get: function(target, prop) {
    console.log("get", prop);
    return Reflect.get(target, prop);
  }
};
const proxifiedObj = {
    value: 4,
    text: "hi",
    get textVal() {
      return this.proxyInstance.text.repeat(this.proxyInstance.value);
    },
    getTextVal() {
      return this.text.repeat(this.value);
    }
}

obj = proxifiedObj.proxyInstance = new Proxy(proxifiedObj, proxyHandler);



console.log("------- normal func -------")
console.log(obj.getTextVal());
console.log("------- getter func -------")
console.log(obj.textVal);    

console.log(obj.textVal);
get getTextVal 
get text 
get value 
hihihihi 

Update:

Or you could do the same thing by creating a custom Proxy that does the assignment for you
(Note: Proxy class cannot be extended but we can use the constructor return value pattern):

  class InstanceAwareProxy {
      constructor(proxifiedObject, proxyHandler) {
          return proxifiedObject.proxyInstance 
                 = new Proxy(proxifiedObject, proxyHandler);
      }
  }

  obj = new InstanceAwareProxy(proxifiedObj, proxyHandler);

Upvotes: 1

Related Questions