Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Proxy.apply() on Node.js does not work. Is this a bug or am I doing it wrong?

I am using Proxy to Proxy an object. The getter and setter work fine like expected. However, the apply method is never called.

    var p = new Proxy({}, {
    /* getter */
    get(target, name) {
        return target[name]
    },
    /* setter */
    set(target, name, value) {
        target[name] = value
    }, 
    /* supposedly called apply */
    apply(target,that,arg) {
        console.log('apply is called, what to do here?')        
    }
})

This way, I can assign something to p or return something even if it doesn't exist. When I for instance let the getter function return this

get(target, name) {
    return 'getting ' + name
},

and then console.log(p.flappy) I will get the response "getting flappy" even when it doesn't exist.

So far so good but when I try to call flappy doing p.flapppy() it wil throw an error that flappy is not a function.

This is still somewhat obvious because the getter does not return a function. When I let the getter return a function like this

get(target, name) {
    return function() { return 'this is '+name } 
},

I can call the property without it having to exist.

console.log(
    p.flappy() // this is flappy!
)

So when does apply get called? Not in the snippet I just showed here and also not in this case:

p.foo = function() {
    console.log('yay!')
    return 'foo!'
}

It does not work to do p.foo() or p.foo.call() or p.foo.apply(), in neither cases apply is called.

The ultimate purpose of this journey is that I want to return a DIFFERENT value depending on whether a property is being read or being called. Like this:

   p.someNewProperty // return what the getter function returns

    p.anotherProperty() // return something else here because this is a function call

Is this possible?

like image 755
Jochem Stoel Avatar asked Jun 05 '16 20:06

Jochem Stoel


People also ask

When node JS should not be used?

Applications with heavy computing server-side. Since Node. js uses only one CPU core, heavy computations on the server will block all other requests. In this case, the event-driven non-blocking I/O model which is the strongest side of Node. js will become useless, and the application performance will suffer.

What is Enoent error in Nodejs?

The code ENOENT means that npm fails to open a file or directory that's required for executing the command. The npm start command is used to run the start script in the package. json file.25-Jun-2022.

What is the use of proxy in JavaScript?

In JavaScript, proxies (proxy object) are used to wrap an object and redefine various operations into the object such as reading, insertion, validation, etc. Proxy allows you to add custom behavior to an object or a function.


2 Answers

I know this is question is a year old, but I ran into this as well and I found a way to do what you are trying to do. So this is for future reference, as I didn't find correct solutions elsewhere.

Short version: accessing functions inside an object (or a class) is essentially getting the property of the object that has the function. The trick is to return another Proxy with apply so you can proxy these functions correctly.

Consider the following object:

const myObject = {
  a: 'Hello world!',
  b: x => x * x
};

Accessing a or b shall both be caught by a Proxy's get, because they are properties of the object. You should catch all get and then filter for functions. Once you have a function, you return a new Proxy that catches this particular function with Proxy.apply. Then, to let the function execute as intended, inside the Proxy.apply we return a Reflect.apply, which calls the original function with the correct arguments as expected.

You will end up with this code:

const myProxyObject = new Proxy(myObject, {
  get(target, propKey, receiver) {    
    // Calling functions
    if (typeof target[propKey] === 'function') {
      return new Proxy(target[propKey], {
        apply(applyTarget, thisArg, args) {
          console.log(`Calling ${thisArg.constructor.name}.${propKey}(${args})`);
          return Reflect.apply(applyTarget, thisArg, args);
        }
      });
    }

    // Accessing properties
    if (target.hasOwnProperty(propKey)) {
      console.log(`Get value of ${target.constructor.name}.${propKey}`);
      console.log(`Value: ${target[propKey]}`);
    }

    return target[propKey];
  }
});

Demo on jsfiddle

You don't get the result of the function, because that would require you to execute it.

Note: it is possible to use this with classes and it works very nicely. The only caveat is that your Proxy will be catching all internal functions as well. In order to prevent logging dozens of valueOfs, I highly recommend to test if a function is native or not with something like this isNative function

like image 131
Mischa Rodermond Avatar answered Nov 04 '22 13:11

Mischa Rodermond


As documented on MDN, the apply proxy method is for proxying a function call on the proxy object itself, not a call on a method of the object.

It only works with functions (as the Proxy target), not regular object instances, but here is how it would work:

var p = new Proxy(function() {}, {
    apply: function() {
        console.log('apply called');
    }
});
p();

The ultimate purpose of this journey is that I want to return a DIFFERENT value depending on whether a property is being read or being called.

It is not possible to directly do what you intend, nor would it really make sense. To call is to read the property.

like image 27
Alexander O'Mara Avatar answered Nov 04 '22 13:11

Alexander O'Mara