Kyle Kinkade
Kyle Kinkade

Reputation: 167

Using ES6 Proxy results in any function call "is not a function"

I was trying to be a sneaky developer, and I done got myself lost in ES6 Proxies. Basically I wanted to capture any get or set in a property from another class I wrote and make sure they are stored somewhere else besides the object. It looks like this:

'use strict'
class MyObject()
{
    superAwesomeFunction(){//super awesome code goes here}
}

const myProxyObject = new Proxy(MyObject, {
get: function(target, name, receiver)
{
    if([CONDITIONS THAT MEET MY DEMANDS])
    {
         // deliver property values if they are
         // my super cool database (if they exist)
    }

    // else just ya know, forward it to the actual MyObject
    // property, if it's an actual property on MyObject
    return target[name];
},
set: function(target, name, value)
{
    if([CONDITIONS THAT MEET MY DEMANDS])
    {
         // set property values if they are
         // to super cool database
    }
    else
    {
        // just set the value to the property, which
        // gets created if it doesn't exist (WHICH IS SO COOL)
        target[name] = value;
    }
    return true;
}

Okay the cool part about this? you can do something like:

// property that doesn't exist (yet)
console.log(myProxyObject.wholivesinapineappleunderthesea);

// but wait for it...
myProxyObject.wholivesinapineappleunderthesea = 'spongebob';

// bam! now it exists on the object, AND lives in my DB!
console.log(myProxyObject.wholivesinapineappleunderthesea);

Which, while a lot of work for something dorky, i cannot explain how happy it makes me feel. However, there is a problem with this. Remember that superAwesomeFunction() I put in MyObject()? Well whenever I try to call it now ES6 gives me this load of sadness:

myProxyObject.superAwesomeFunction is not a function

ES6 sits on a throne of LIES! It's totally there right? Okay so I'm pretty sure I'm trapping something wrong, because when I debug I see that the get section of the Proxy is actually picking up the superAwesomeFunction call (which makes sense as superAwesomeFunction is a property that contains a function (){})

HERE'S MY QUESTION: Does anybody know of any solution that will let me keep my ridiculous on the fly properties and still call my superAwesomeFunction()? Is it the apply trap?

Upvotes: 1

Views: 5827

Answers (3)

vasilyrud
vasilyrud

Reputation: 855

To extend on Sam's answer.

The following succeeds:

function Foo() {
  this.bar = function() { console.log('baz'); }
}
let foo = new Proxy(new Foo(), {});
foo.bar();

However, when the get trap is added to the handler, "is not a function" is thrown:

function Foo() {
  this.bar = function() { console.log('baz'); }
}
let foo = new Proxy(new Foo(), {
  get: function(target, prop) {}
});
foo.bar();

The solution here is to add a typeof check for default handling of the target's existing functions:

function Foo() {
  this.bar = function() { console.log('baz'); }
}
let foo = new Proxy(new Foo(), {
  get: function(target, prop) {
    if (typeof target[prop] === 'function') {
      return target[prop].bind(target);
    }
  }
});
foo.bar();

The following articles gives some more examples and provides nice boilerplate to use with Proxy handlers: Safely Extending The JavaScript Set Object Using Proxies

Upvotes: 4

Kyle Kinkade
Kyle Kinkade

Reputation: 167

Full code for answer is here, thank you everyone!

'use strict'
class MyObject()
{
    superAwesomeFunction(){
        console.log("this works!");
    }
}

const myProxyObject = new Proxy( new MyObject(), {
get: function(target, name, receiver)
{
    if([CONDITIONS THAT MEET MY DEMANDS])
    {
        // do something cool for yourself
    }

    return target[name];
},
set: function(target, name, value)
{
    if([CONDITIONS THAT MEET MY DEMANDS])
    {
        // do something cool for yourself
    }
    else
    {
        // just set the value to the property, which
        // gets created if it doesn't exist (WHICH IS SO COOL)
        target[name] = value;
    }
    return true;
}
});

Upvotes: 0

Sam Redway
Sam Redway

Reputation: 8127

It is a little syntax error there ... you are wrapping the proxy around the raw class, not an object.

Try:

const myProxyObject = new Proxy(new MyObject(), { ... });

This should be ok. For example:

> class Blah {
... run() {
..... console.log('yay')
..... }
... }
[Function: blah]
> myProx = new Proxy(new Blah(), {});
blah {}
> myProx.run()
yay

Upvotes: 2

Related Questions