3

I have a proxy handler like this:

let handler = {
        get: function(target, name) {
            let member = target[name];
            if (typeof member === 'function') {
                return function() {
                    //
                }
            }
            return member;
        }
    }

Whenever a method is called on Proxy object:

var obj = {
    foo: function() {
        //
    }
}

var p = new Proxy(obj, handler);
p.foo();

...It invokes the function that was returned from the handler's getter. But the problem is, when the method is accessed rather than invoked like this:

p.foo;

The entire definition of the function is returned.

Is there any way by which I could check to see if the method is being accessed (p.foo) or invoked (p.foo())? I am trying to achieve something like:

if (typeof member === 'function' && member.isBeingCalled()) {
    return function() {
        //
    }
}

Also, in case of p.foo, I would like to return the definition of member instead of the function that handler's getter returns.

Tanmay
  • 3,009
  • 9
  • 53
  • 83

1 Answers1

2

Yes, this is possible but in order to make it work you need to consider what happens when call a method

 o.foo()

as there is more going on than you might think.

The first operation is a property lookup that retrieves the value of the property having the key "foo" from the object o. Now, the value retrieved is itself an object, specifically a function object.

The second operation is the invocation of that function as a method by way of function.prototype.apply. What this does is invoke the function with its this value bound to o.

Therefore, in order to achieve the desired behavior, one must proxy the value of the property o.foo rather than o itself. your proxy will use the apply trap to intercept and modify the invocation of the function what is the value of that property. This is described well by MDN. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/apply

Essentially you would write something like

const foo = o.foo;
const proxiedFoo = new Proxy(foo, {
  apply(target, thisArg, argumentsList) {
    if (thisArg) {
      // this function is being invoked as a method
    }
  }
});

o.foo = proxiedFoo;
Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
  • Thanks for your answer. The problem with this is that from the perspective of an API consumer, I might not want to explicitly call apply `proxiedFoo.apply(o)`. I would expect `proxiedFoo()` to automatically go through the `if (thisArg){}` block. – Tanmay Jun 02 '19 at 08:41
  • If I understand your intent correctly it will. The name of Proxy's apply trap is misleading. – Aluan Haddad Jun 02 '19 at 09:15
  • Please take a look: https://jsbin.com/dukoqik/1/edit?js,console,output – Tanmay Jun 02 '19 at 13:43