类型错误:在将实例包装为Proxy时,无法从其类未声明的对象读取私有成员

前端开发 2026-07-09

我正在尝试用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

我尝试过的

  1. 在处理程序内部把 value.apply(this, args) 改成 value.apply(target, args)。这实际上会 修复 错误,但它破坏了代理拦截后续进一步嵌套操作或内部方法链的能力,因为 this 会回到原始的 target 而不是保持在 receiver(代理)。
  2. 我尝试使用 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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章