Search code examples
javascriptnode.jses6-proxy

Create Proxy for prototype method


I am fumbling for a way to avoid creating a new Proxy for each new object instance. I have 1 prototype, and I want to use just 1 Proxy. That's the goal. If I use a Proxy per instance, I could have thousands and thousands of Proxy objects which hurts performance a lot.

Ultimately what I am doing is setting properties on a prototype method, like this:

const v = {
  foo: function () {
    assert.notEqual(this, v);
  }
};

v.foo.bar = function(){
   // I need `this` to be a certain value here
};

v.foo.zam = function(){
   // I need `this` to be a certain value here
};

but I need those properties to still have the same context (this value), as the prototype method itself would.

const assert = require('assert');

const v = {
  foo: function () {
    assert.notEqual(this, v);
  }
};

new Proxy(v.foo, {
  get: function(target, prop){
     console.log(this); // I am looking to get access to `x` here
  }
});


const x = Object.create(v);

x.foo();
const z = x.foo.bar; // I would have guessed this would have fired the Proxy `get` method.

I am trying to do some black magic, where I can access the this value of the v prototype methods from the Proxy. In this case, that would mean accessing the value of x from the Proxy. Is this possible somehow? Also, I can't figure out why the get method of the Proxy is not called when I read the bar property from x.foo, as in x.foo.bar.

this Github gist I just created is a little bit closer: https://gist.github.com/ORESoftware/757dd6285d554f4f52ae415fd39141a5

however, I still don't think it's possible to do what I want to do. The rationale is so that I can reuse the same Proxy object in the prototype, without having to create a new Proxy for each instance.


Solution

  • Some black magic here, but it works, you can use an Object.defineProperty getter to set the context for the proxied prototype method, (note this methodology will only work for synchronous parts of your code).

    const proto = {};  // the prototype object 
    
    const ctx = {
       val: null
    };
    
    const foo = function () {
        // the prototype method
        // do something with ctx.val which gets set dynamically
    };
    
    foo.bar = function(){
      // doing important stuff here
      // do something with ctx.val which gets set dynamically
    };
    
    const p = new Proxy(foo, {
      get: function (target, prop) {
    
        if (typeof prop === 'symbol') {
          return Reflect.get.apply(Reflect, arguments);
        }
    
        // do something with ctx.val
        // => ctx.val
    
      }
    });
    
    
    Object.defineProperty(proto, 'foo', {
      get: function() {
        ctx.val = this;  // set the context here!!
        return p;
      }
    });
    

    now we can use it like so:

    proto.foo.bar()
    

    when foo is accessed, it then dynamically sets the ctx for bar()

    I end up using it like so:

    const o = Object.create(proto);
    o.foo.bar();
    

    And we can also call o.foo() if we want to.