ARTICLE AD BOX
I am trying to build a simple, generic performance-logging wrapper using JavaScript Proxy. The goal is to intercept method calls on any class instance and log how long they take to execute.
It works flawlessly on standard classes. However, the moment I try to use this proxy wrapper on a modern class that utilizes native private fields (#), the code explodes with a cryptic TypeError.
Here is a minimal reproducible example of my setup:
class UserRepository { #databaseUrl; // Private field #connection; constructor(url) { this.#databaseUrl = url; this.#connection = "Active Connection to " + url; } fetchUser(id) { // Accessing the private field here causes the crash console.log(`Connecting via: ${this.#databaseUrl}`); return { id, name: "Alice" }; } } // The Proxy Handler to log execution time const loggingHandler = { get(target, prop, receiver) { const value = Reflect.get(target, prop, receiver); if (typeof value === 'function') { return function(...args) { console.time(`Execution of ${prop}`); const result = value.apply(this, args); // <-- Error points here console.timeEnd(`Execution of ${prop}`); return result; }; } return value; } }; // Instantiating and wrapping in the Proxy const realRepo = new UserRepository("https://db.example.com"); const proxiedRepo = new Proxy(realRepo, loggingHandler); // Triggering the method proxiedRepo.fetchUser(1);When I execute proxiedRepo.fetchUser(1), the engine throws this error:
TypeError: Cannot read private member #databaseUrl from an object whose class did not declare it at UserRepository.fetchUser (test.js:12:41) at Object.value (test.js:25:38) at test.js:35:13What I've Tried
Changing value.apply(this, args) to value.apply(target, args) inside the handler. This actually fixes the error, but it breaks the proxy's ability to intercept any further nested operations or internal method chains because this reverts back to the raw target instead of staying on the receiver (the proxy).
I tried using Reflect.ownKeys() to see if I could map the private fields, but they don't show up (which makes sense, they are private).
Why is the Proxy breaking JavaScript's internal mechanism for private fields, and how can I fix this handler without completely losing the this context of the proxy wrapper?
