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?
Ah but the get trap also has a receiver. So maybe it could work like this?
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.
This is the pattern that I've seen existing code use:
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:
const p = makeProxy(new Person("test"));
p.sayHello(); // works
@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?
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