# New Proxy trap for method application

**URL:** <https://es.discourse.group/t/new-proxy-trap-for-method-application/2531>\
**Category:** 💡 Ideas\
**Created:** [April 1, 2026, 4:22am UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531 "2026-04-01T04:22:13Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 1, 2026, 4:22am UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/1 "2026-04-01T04:22:13Z")

</div>

```javascript
class Person {
  #name;
  constructor(name) {
    this.#name = name;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.#name}`);
  }
}

let person = new Proxy(new Person(), {
  applyMethod: (target, receiver, args, receiverTarget) => {
    return target.apply(receiverTarget, args);
  }
});

person.sayHello(); // it works!

```

The idea is to ensure that proxies are not incompatible with class private fields. @LeaVerou is this an idea that might interest you?

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 1, 2026, 4:54am UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/2 "2026-04-01T04:54:13Z")

</div>

Ah but the `get` trap also has a receiver. So maybe it could work like this?

```javascript
let person = new Proxy(new Person(), {
  getReceiver: (object, key, value, valueTarget) => {
    return valueTarget;
  }
});

```

I also like that this ensures that `handlers.call` still maps 1:1 to the `[[Call]]` internal method.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [April 1, 2026, 6:38am UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/3 "2026-04-01T06:38:15Z")

</div>

This is the pattern that I've seen existing code use:

```javascript
const originalObjects = new WeakMap(); // proxy -> original
const proxies = new WeakMap(); // original -> proxy

function unwrap(value) {
    return originalObjects.get(value) ?? value;
}

const handler = {
    get(target, prop, receiver) {
        const realTarget = unwrap(target);
        const realReceiver = unwrap(receiver);
        const value = Reflect.get(realTarget, prop, realReceiver);
        return makeProxy(value);
    },
    apply(target, thisArg, args) {
        const realTarget = unwrap(target);
        const realThis = unwrap(thisArg);
        const realArgs = args.map(unwrap);
        const value = Reflect.apply(realTarget, realThis, realArgs);
        return makeProxy(value);
    }
};

function makeProxy(o) {
    if (o == null) return o;
    if (typeof o !== "object" && typeof o !== "function") return o;
    
    if (originalObjects.has(o)) return o;
    if (proxies.has(o)) return proxies.get(o);
    
    const p = new Proxy(o, handler);
    originalObjects.set(p, o);
    proxies.set(o, p);
    return p;
}

```

So then:

```javascript
const p = makeProxy(new Person("test"));
p.sayHello(); // works

```

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 1, 2026, 12:46pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/4 "2026-04-01T12:46:17Z")

</div>

@aclaymore I agree that the code you wrote does what it says it does. I also know from having already tried an approach like this that its performance does not scale well, not even to millions of object instances.

So the question is: does this pattern deserve _support_ or is anyone wanting to be able to do this so deviant that they deserve to have a bad a experience fighting to make the language do something it wasn't meant to?

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 1, 2026, 1:17pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/5 "2026-04-01T13:17:15Z")

</div>

I do think it's a good sign for a proposal if it is possible to make a robust polyfill. Then the syntax can be desugared as a method of support for engines without runtime-level support.

My argument is that this isn't a weird obscure thing where a slow 20 line workaround is good enough. These are two common features of the core language. We already know that people are trying to do very average stuff and ending up surprised at the complete breakage that results when these two features touch each other...

Yes the polyfill will still be slow at first, but only if we make a standard can the engines actually support it, and only with engine support will this be fast enough to use at scale

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [September 28, 2026, 6:04am UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/6 "2026-09-28T06:04:45Z")

</div>

I don't understand what you're trying to propose here. A receiver is only one of the few ways the proxy & its target identities can be observed as different, so a trap for it wouldn't actually solve all observability use cases.

You can basically already simulate what the receiver trap would do by having the proxy bind all functions to the target, or at least wrap them in a "forwarder" that replaces the proxy receiver with the target. But as @aclaymore code shows you have to do more than that because really the args could contain proxies too. And then really you probably should wrap return values too, or else the caller might get confused to see non proxied values. And all the sudden you're back at building a full membrane.

There are basically 2 working use cases for proxies: objects you create as proxy to have dynamic / exotic behavior and where the target is either a dummy or complicit, and membranes. Anything else is just gonna break. Trying to insert proxies in object graphs which don't expect it without shielding that graph from being able to observe the proxies identities is a fools errand.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 12:21pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/7 "2026-09-28T12:21:38Z")

</div>

I'm just trying to make the layer transparent so that at a basic level wrapping class instances doesn't break them:

```js
class Test {
  #foo;

  method() {
    return this.#foo;
  }
}

let { getReceiver } = Reflect;
let t = new Proxy(new Test(), { getReceiver });

t.method(); // not an error anymore!

```

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 1:19pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/8 "2026-09-28T13:19:43Z")

</div>

What you're saying is that things can still get messed up:

```js
class IterableIterator {
  Symbol.iterator() {
    // the proposal changes "this" here from proxy to no-proxy
    return this;
  }

  next() {
  }
}

let iterable = new Proxy(new IterableIterator(), {});

let iter = iterable[Symbol.iterator]();

// we broke the transparentness of wrapping!
// iter is now the un-proxied class, and we can't intercept this call
iter.next();

```

As you mention arguments can be subject to the same problems as return values too.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 1:35pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/9 "2026-09-28T13:35:42Z")

</div>

I think it makes sense to zero in on what is and isn't different. What isn't different is that either way, if your purpose is to create a membrane you have to do it. You have to wrap and unwrap everything that needs to be wrapped an unwrapped in arguments and return values. The point of `Proxy` is that it gives you a place to write your wrapping and unwrapping logic.

If we set that aside then, supporting `getReceiver` would just make it easier to create (performant, correct) membranes, would it not?

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [September 28, 2026, 1:44pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/10 "2026-09-28T13:44:01Z")

</div>

The Proxy hooks directly map to the Meta Object Protocol that describes how objects can be interacted with. Every object in the language implements this protocol internally.

Proxy is a low level API that lets you intercept the MOP.

There is no "getReciever" MOP method. Adding one would be a very substantial change to the very core of the language and all of its implications would need to be thought through.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 1:59pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/11 "2026-09-28T13:59:52Z")

</div>

I don't dispute any of that, but it would seem to be absolutely critical that any Meta Object Protocol actually captures all the basic interactions that happen with objects. The _point_ of such a protocol is to model and capture all such interactions.

Well, this one of the things the real object system does. It does receiver lookup (`receiver.method()`) based on whatever is to the left of the dot. So too should the model support doing that. _How_ exactly the model represents it I don't much care, only _that_ this behavior of the object system is modeled.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [September 28, 2026, 2:10pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/12 "2026-09-28T14:10:13Z")

</div>

The receiver isn't looked up though, it is baked into the syntax. Meaning it is also baked into every engine and into each individual JIT tier level.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 2:40pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/13 "2026-09-28T14:40:02Z")

</div>

Proxy hooking must be baked into each JIT level too, right?

The lexical receiver must by definition be the proxy, and the proxy knows its own target (the inner receiver). There shouldn't be any costly lookup needed. You're already broken out of pure optimized evaluation to run the hook's script instead of the JIT optimized code for native monomorphic object property access.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [September 28, 2026, 2:57pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/14 "2026-09-28T14:57:51Z")

</div>

Maybe it won't be too complex or add overhead but I suspect only trying to implement it in at least one of the main engines would demonstrate either way so that could be weighed up against the problem it's trying to solve. And also how much faster it would actually make membranes, if that's the goal. JS performance characteristics can be hard to predict on theory alone.

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [September 28, 2026, 3:49pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/15 "2026-09-28T15:49:33Z")

</div>

I suspect the answer here is not a new trap to help resolve the receiver, but instead some abstraction to help create membranes. At the core all membranes must wrap/unwrap and map their argument. I'm honestly not sure what an API for this would look like, especially in a world of LLM assisted programming that can probably whip up the actual wiring for the exact use case.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 4:25pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/16 "2026-09-28T16:25:47Z")

</div>

Yes, if we design well an LLM will eventually be able to recite the design in an instant. That relieves none of the pressure on us to design well.

I'm pretty sure the answer is a new trap, because it's the smallest change that engines can make to let users create membranes efficiently (when private properties are involved).

@aclaymore offered a solution that technically works, but I wouldn't want to see LLMs copypasta this everywhere even if they could. For one think it doesn't perform well. I've learned the hard way that while the normal JS object system can easily handle millions of objects, weak maps cannot.

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [September 28, 2026, 8:09pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/17 "2026-09-28T20:09:49Z")

</div>

But you have to use a WeakMap, at least in the target -\> proxy lookup direction. There is no way around that. All an engine native implementation would do is also use a WeakMap internally.

For the other direction the handler traps already have access to the target.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 11:15pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/18 "2026-09-28T23:15:39Z")

</div>

> But you have to use a WeakMap, at least in the target -\> proxy lookup direction.

The thing is, if you do that you end up with a system that can't scale to millions of objects.

I built the exact system you're talking about which used WeakMap mappings on millions of objects, and it was a performance catastrophe of the first order. There were seconds-long garbage collections...

So I don't see that as a way forward. An implementation of a low-level feature needs to have low-level-appropriate performance to be broadly useful, and that's where this falls down.

That's why I'm also here advocating for `PropertyWeakMap`. If you attached the identity-stable proxy to its target using an internal property symbol, then there's no more weak maps at all and the result would be a membrane that can scale to millions of objects.

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [September 28, 2026, 11:31pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/19 "2026-09-28T23:31:51Z")

</div>

> [@conartist6](#):
>
> There were seconds-long garbage collections...

This sounds like an engine issue. The GC shouldn't be pausing for multiple seconds.

Note: just because something is slow it doesn't necessarily mean that the language needs a new API, it might mean that the use case hasn't been a priority to optimize. If the goal is performance, we should first rule out that nothing can be done with the current language.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [September 28, 2026, 11:52pm UTC](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531/20 "2026-09-28T23:52:11Z")

</div>

Also engines shouldn't have superlinear performance penalties for big weak maps, but as far as I can tell v8 does. This behavior was observed with runs of BABLR and BABLR outputs streaming log entries as it goes. You could sit there and watch it slowly dying: the log output coming slower and slower and slower and slower over time. The perf traces showed it: eventually 75% of the processing time was being spent in `WeakMap.prototype.set` (and GCs), where these started out as more like 1% of the overall time. I can put together a small repo reproducing the issue in the next few days here.

I agree that it's hard to say what this means for direction until I understand more fully what is happening and why.

But this is why I see a strong draw to a design that doesn't need any weak maps to work: it's ready to be used today, where engine changes would take years. (edit: this doesn't entirely make sense as how it's done today would likely need to use a weakmap in its polyfill)

[Next page](https://es.discourse.group/t/new-proxy-trap-for-method-application/2531.md?page=2)
