I’d like to allow dynamic method calls on a JavaScript object, i.e. even if the object does not have the method defined, it should still be invokable (e.g. with a default dummy).
const obj = {a: 5};
obj.dynamicMethodCallWithAnyNameYouWant(); // should work
obj.a; // should work, value is 5
obj.b; // should yield undefined
Now with a JS Proxy, we can do something like this to get in the right direction:
const obj = new Proxy({a: 5}, {
get(target, prop, receiver) {
// If the property does not exist, define it as a new function
if (!(prop in target)) {
target[prop] = function() {
console.log(`Method ${prop} has been dynamically created and invoked!`);
};
}
return Reflect.get(target, prop, receiver);
}
});
obj.dynamicMethodCallWithAnyNameYouWant();
console.log(obj.a); // prints 5
const res = obj.thisShouldYieldUndefinedInsteadOfCreatingAFunction
res(); // this prints "Method thisShouldYieldUndefined ..."
The problem is that we cannot detect whether the property was accessed or called since
[…] JavaScript does not distinguish between attributes and methods. It’s all just properties that are accessed, and their value can be called if it’s a function. ~ this answer
Is there another way to achieve the desired behavior, e.g. with an alternative approach to proxies?
2
Answers
No, there is no other way, this is impossible. If
obj.b
yieldsundefined
, thenobj.b()
will throw an exception. Ifobj.dynamicMethodCallWithAnyNameYouWant()
is a valid method call, thenobj.dynamicMethodCallWithAnyNameYouWant
will have to return a function and notundefined
. Doesn’t matter whetherobj
is a proxy or something else.No, it’s not possible.
Doing
A.b()
is semantically equivalent toBy the time the system is going to do
tmp()
, it no longer is relevant iftmp
came from a proxy or from anywhere else. And the proxy has no way to see what you want to do with the result that it produces.The closest you can get to what you want is by consistently doing
A.b?.()
instead, so using optional chaining.