3

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.

user3840170
  • 26,597
  • 4
  • 30
  • 62
Alexander Mills
  • 90,741
  • 139
  • 482
  • 817

3 Answers3

2

You're not looking for a proxy, all you need is a simple getter:

const v = {
  get foo() {
    console.log(this); // will log x
    return Object.assign(() => "hi", {bar: 42});
  }
};

const x = Object.create(v);

console.log(x.foo());
console.log(x.foo.bar);

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.

For that you would need to use the proxy as the prototype. Your current code is just creating a proxy object and then throwing it away, which will not affect the original code in any way. You'd rather need to do something like

const p = new Proxy({}, {
  get(target, prop) {
    console.log(target); // will log x
    if (prop == "foo")
      return Object.assign(() => "hi", {bar: 42});
  }
});

const x = Object.create(p);
//                      ^ use the proxy here!

console.log(x.foo());
console.log(x.foo.bar);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I was thinking about that, let me try it – Alexander Mills Dec 13 '17 at 06:05
  • I have pretty stupid/clever idea that will just take advantage of JS single-threaded nature - I can just set a context value when a property is accessed, that should work, will add an answer later. This is all synchronous code, nothing async, so I can some more black magic. – Alexander Mills Dec 13 '17 at 06:08
  • @AlexanderMills [What problem](https://meta.stackexchange.com/q/66377) are you trying to solve? – Bergi Dec 13 '17 at 06:09
  • I am simply trying to avoid creating a new Proxy for each 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. – Alexander Mills Dec 13 '17 at 06:11
  • OK, but that's rather standard usage not black magic :-) Still, why are you using proxies at all? – Bergi Dec 13 '17 at 06:27
  • my use case is totally out there...I will add an answer explaining how I solved it – Alexander Mills Dec 13 '17 at 06:49
  • just added it, it works - the Proxy is still useful, but not as imperative as it was before. – Alexander Mills Dec 13 '17 at 06:55
  • I am proxy-ing the assert library ... instead of defining a 100 methods, I can use 1 Proxy...hard to explain – Alexander Mills Dec 13 '17 at 07:00
2

You can also add the proxy to the object prototype.

SomeObject.prototype = new Proxy(
    SomeObject.prototype,
    SomeObjectHandlerProxy
);

instance = new SomeObject();

something like should work

Luis Neves
  • 821
  • 7
  • 11
  • Note that for built-in objects `prototype` is not writable: https://stackoverflow.com/a/76366833/10406353 – WofWca Jun 05 '23 at 10:48
0

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.

Alexander Mills
  • 90,741
  • 139
  • 482
  • 817