skip to Main Content

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


  1. No, there is no other way, this is impossible. If obj.b yields undefined, then obj.b() will throw an exception. If obj.dynamicMethodCallWithAnyNameYouWant() is a valid method call, then obj.dynamicMethodCallWithAnyNameYouWant will have to return a function and not undefined. Doesn’t matter whether obj is a proxy or something else.

    Login or Signup to reply.
  2. No, it’s not possible.

    Doing A.b() is semantically equivalent to

    const tmp = A.b;
    tmp();
    

    By the time the system is going to do tmp(), it no longer is relevant if tmp 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search