类型错误:在将实例包装为Proxy时,无法从其类未声明的对象读取私有成员
我正在尝试用JavaScript Proxy 构建一个简单、通用的性能日志包装器。目标是在任意类实例上拦截方法调用,并记录它们执行所花的时间。
在标准类上它运作得非常完美。然而,一旦我尝试在使用了 原生私有字段 (#) 的现代类上使用这个代理包装器,代码就会因为一个莫名其妙的 TypeError 而崩溃。
以下是我的设置的一个最小化的可复现示例:
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);
当我执行 proxiedRepo.fetchUser(1) 时,引擎会抛出这个错误:
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:13
我尝试过的
- 在处理程序内部把
value.apply(this, args)改成value.apply(target, args)。这实际上会 修复 错误,但它破坏了代理拦截后续进一步嵌套操作或内部方法链的能力,因为this会回到原始的target而不是保持在receiver(代理)。 - 我尝试使用
Reflect.ownKeys()看能否映射私有字段,但它们没有显示出来(这很合理,因为它们是私有的)。
为什么代理会破坏JavaScript的私有字段的内部机制,以及如何在不完全失去 this 上下文的情况下修复这个处理程序?
解决方案
当你的方法运行 value.apply(this, args) 时,this 上下文指向的是代理,而不是原始对象。
因为代理并不拥有那些私有内存槽,运行时引擎会抛出一个 TypeError。
仅在调用使用私有属性的方法时,传递原始的 target 上下文:
const loggingHandler = {
get(target, prop, receiver) {
// Read directly from target to avoid proxy loop traps
const value = Reflect.get(target, prop, target);
if (typeof value === 'function') {
return function(...args) {
console.time(`Execution of ${prop}`);
// Run method using the raw target to satisfy private slot checks
const result = value.apply(target, args);
console.timeEnd(`Execution of ${prop}`);
return result;
};
}
// Normal properties continue using the proxy context
return Reflect.get(target, prop, receiver);
}
};
把 target 传给初始的 Reflect.get,可以把方法查找隔离开来。如果类动态调用内部方法,它就不会触发递归的堆栈溢出。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。