7

What's the equivalent of the __call magic method from PHP ?

I was under the impression that Proxy can do this, but it can't.

class MyClass{
  constructor(){
    return new Proxy(this, {
      apply: function(target, thisArg, args){
        console.log('call', thisArg, args);
        return 'test';
      },

      get: function(target, prop){
        console.log('get', prop, arguments);
      }


    });

  }

}

var inst = new MyClass();
console.log(inst.foo(123));

get seems to work because I see "get foo", but apply does not. I get is not a function error.

3

3 Answers 3

9

apply actually handles a function call to the object itself, i.e. if you do new Proxy(someFunction, { apply: ... }), apply would be called before someFunction is called.

There is nothing for trapping a call to a property, because this would be superfluous – get already handles when a property is returned. You can simply return a function that then produces some debug output when called.

class MyClass{
  constructor(){
    return new Proxy(this, {
      get: function(target, prop) {
        return function() {
          console.log('function call', prop, arguments);
          return 42;
        };
      }
    });
  }
}

var inst = new MyClass();
console.log(inst.foo(123));

4
  • I don't think this is a complete solution. you have returned a static value. you need to use apply inside get to calculate result of original function. Commented Jan 13, 2019 at 19:40
  • 1
    @BadisMerabet I'm sure OP can modify the function to suit their needs. Nevertheless the question was an analogue to __call in PHP, which is used to resolve non-existent functions of an object, i.e. there is no "original function" to call. The default / intended behaviour of Proxy is well-documented already. Commented Jan 13, 2019 at 20:01
  • thank you for clarifying this, It makes sense. Still I am interested in what's the goal or benefits of intercepting calls to non existing functions in a piece of code or software ? Commented Jan 16, 2019 at 13:30
  • @BadisMerabet I know that this sort of "magic" (comparable to macros in some languages, and runtime reflection in others) can be used to implement e.g. ORM systems, where methods may be generated dynamically for one-to-many relations. Some other uses I can think of – a library with lazily loaded modules/functions, libraries which have (too) many functions that all have a given pattern (e.g. a geometry library – addPoint2D, addPoint3D, …). There are lots of applications, and perhaps they are not indicative of "good" code always, but this is Javascript after all. :) Commented Jan 16, 2019 at 20:17
4

This another way of achieving what you have requested.

class MyClass{
  constructor(){
     return new Proxy(this, {
        get(target, propKey, receiver) {
            const origMethod = target[propKey];
            return function (...args) {
                let result = origMethod.apply(this, args);
                console.log(propKey + JSON.stringify(args)
                    + ' -> ' + JSON.stringify(result));
                return result;
            };
        }
    });
  }
  
foo = (x) => {
  return x + 1;
};

}

var inst = new MyClass();
console.log(inst.foo(123));

2
  • Great ! This intercepts all method calls to an object, even if a method does not actually exist. Commented Nov 28, 2021 at 21:54
  • Your code should be marked as the correct answer. It includes the redirect to the class methods. Thanks for such clean implementation Commented Jan 3, 2023 at 16:45
2

Yeah Proxy can do that, but even when trapping methods you have to use get of Proxy.

Then here I also executes your real method, but I don't know if you want to mock it.

class MyClass {
  constructor() {
    return new Proxy(this, {
      get(target, prop, receiver) {
        if (typeof target[prop] !== "function") {
          return "etcetcetc";
        }
        return function(...args) {
          console.log('call', args);
          return target[prop]();
        };
      }
    });
  }

  foo() {
    console.log('I am foo!');
  }
}

var inst = new MyClass();
inst.foo(123);

As you can see, if you are calling a method of your instance, I will intercept it, and then return your original method execution.

If you are accessing an attribute of your instance, I will return always a mocked string.

Then of course change it with the behavior that you want to.

Not the answer you're looking for? Browse other questions tagged or ask your own question.