I have the following code, where I'm using Proxy object (proxy) to try to catch method calls and access of the properties:
Example: https://jsfiddle.net/r8j4fzxL/2/
(function() {
'use strict';
console.clear();
//some empty class where I want to trap methods props
class X {
//...
}
let proxy = {
get: function(target, prop, receiver) {
console.log('get called: ',
'target:', target,
'prop:', prop,
'receiver:', receiver
);
//this is OK, if we are called as a method.
//but it isn't when called as .prop - because, obviously, we return a function here.
return function(...args) {
console.log('wrapper args:', args);
return 42;
}
},
};
let p1 = new Proxy(X, proxy);
//how to distinguish the two in the above proxy:
console.log(p1.test('some arg passed'));
console.log(p1.test);
})();
And I have two questions here.
Generally, is this the right way, if I want to trap both properties access and method access? Or maybe should I go with .apply trap somehow (failed to do so though)?
If this is the right way (using .get) - then how do I know how the user accessed the... thing? Via .foo;
or via .foo()
;?
Resources I used and apparently didn't fully understand:
- MDN docs: Proxy
- MDN docs: handler.get()
- MDN docs: handler.apply()
SO: JavaScript Equivalent Of PHP __call
This is actually a solution for this question (which was marked as duplicate of this but is not !!): How to get function arguments in a Proxy handler
You cannot get the arguments in a "get" trap, because when the get trap is called the functions is not called yet!
But you can create another proxy with "apply" trap, here is the example:
(function() {
'use strict';
console.clear();
//some empty class where I want to trap methods & props
class X {
}
let proxy = {
get: function(target, prop, receiver) {
console.log(arguments);//this gives the arguments of the 'get' trap itself.
// normally you trap an existent funcion, in this example we are creating a new one here
var F = function(...args){
console.log('Original function call', args);
}
return new Proxy(F, {
apply: function(target, thisArg, argumentsList) {
// here you have the arguments
console.log('Trapped function call', argumentsList);
return target.apply(thisArg, argumentsList);
}});
},
};
let p = new Proxy(X, proxy);
console.log(p.test('some arg passed'));
})();
So the trick is to trap the function with get first, and instead of returning the original function, to return a Proxy with apply trap to the original function.
Proxies are essentially objects that expose a programmatic way to hook into the operations that objects can have performed on them. With that as a base, there is no way to distinguish a property access from a property access + call, at the point where the property is accessed. The fact that the returned value is a function is all that you can know.
From the standpoint of the language, p.foo()
breaks down into
var p = ...
var foo = p.foo;
foo.apply(p, []);
as an example. p.foo
wouldn't actually access .apply
, but the point is that the property access is entirely independent of how the returned value is actually used/called.
So you option would essentially be to check whether the value of test
was already a function, and if so, wrap that value with your wrapper. If it was not a function, it seems like you'll want to skip wrapping it. e.g.
get: function(target, prop, receiver) {
var value = Reflect.get(target, prop, receiver);
if (typeof value === "function") {
return function(...args) {
// probably call 'value' with whatever you need
};
} else {
// return the origin value.
return value;
}
}