ES6 代理类,访问私有属性无法从类未声明的对象中读取私有成员 #hidden

问题描述

我在玩代理对象、类和私有属性。 并遇到此错误消息:

/home/marc/projects/playground/pipeline/clsss.js:14
        this.#hidden = !this.#hidden;
                             ^

TypeError: Cannot read private member #hidden from an object whose class did not declare it
    at Proxy.toggle (/home/marc/projects/playground/pipeline/clsss.js:14:30)
    at Object.<anonymous> (/home/marc/projects/playground/pipeline/clsss.js:37:19)
    at Module._compile (internal/modules/cjs/loader.js:1118:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1138:10)
    at Module.load (internal/modules/cjs/loader.js:982:32)
    at Function.Module._load (internal/modules/cjs/loader.js:875:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12)
    at internal/main/run_main_module.js:17:47

重现代码

class Parent {

    #hidden;

    constructor() {
        this.#hidden = false;
    }

    get hidden() {
        return this.#hidden;
    }

    toggle() {
        this.#hidden = !this.#hidden;
        console.log("Changed",this.#hidden)
        return this.#hidden;
    }

}


const p = new Parent();
const proxy = new Proxy(p,{
    get: (target,prop,receiver) => {
        return target[prop];
    }
});

console.log(p.toggle())
console.log(proxy.toggle())  // this is the problem
console.log(p.toggle())

有没有办法处理代理类实例上的私有属性

为什么这不适用于代理?

感谢您提供任何提示/答案。

编辑:在 github 上发现了一个相关问题:https://github.com/tc39/proposal-class-fields/issues/106 我发现的一个快速技巧是使用:

const proxy = new Proxy(...,receiver) => {

        // bind context to original object
        if (target[prop] instanceof Function) {
            return target[prop].bind(p);
        }

        return target[prop];

    }
});

但这看起来很不干净/错误

解决方法

这是不可能的,请阅读 Cannot read private member #hidden from an object whose class did not declare it 的错误消息,一旦您创建代理并尝试从中访问某些内容,它将与您正在包装的原始类不具有相同的类。

更新:您可以在此处阅读有关私有字段及其工作原理的更多信息https://developer.cdn.mozilla.net/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields

,

如果这是所需的行为,您可以在构造函数上绑定方法:

constructor() {
      this.#hidden = false;
      this.toggle = this.toggle.bind(this);
}

演示:

class Parent {

    #hidden;

    constructor() {
        this.#hidden = false;
        this.toggle = this.toggle.bind(this);
    }

    get hidden() {
        return this.#hidden;
    }

    toggle() {
        this.#hidden = !this.#hidden;
        console.log("Changed",this.#hidden)
        return this.#hidden;
    }

}


const p = new Parent();
const proxy = new Proxy(p,{
    get: (target,prop,receiver) => {
        return target[prop];
    }
});

console.log(p.toggle())
console.log(proxy.toggle())  // this is the problem
console.log(p.toggle())

否则你可以代理类本身:

class Parent {

    #hidden;

    constructor() {
        this.#hidden = false;
    }

    get hidden() {
        return this.#hidden;
    }

    toggle() {
        this.#hidden = !this.#hidden;
        //console.log("Changed",this.#hidden)
        return this.#hidden;
    }

}


const p = new Parent();
const ParentProxy = new Proxy(Parent,{
    get(target,receiver) {
        return target[prop];
    }
});

const p2 = new ParentProxy();

console.log('p toggle:',p.toggle());
console.log('p2 toggle:',p2.toggle());  //
//console.log(proxy.toggle())  // this is the problem
console.log('p toggle:',p2.toggle());

,

Proxy 对象提供对 targetprop 的直接访问,因此您可以像直接从实例访问一样简单地访问私有。

我的方法是;

class Parent {

    #hidden;

    constructor() {
        this.#hidden = false;
    }

    get hidden() {
        return this.#hidden;
    }

    toggle() {
        this.#hidden = !this.#hidden;
        console.log("Changed",this.#hidden)
        return this.#hidden;
    }

}


var p = new Parent();
var proxy = new Proxy(p,{get: (target,receiver) => _ => target[prop]()});
console.log(p.toggle())
console.log(proxy.toggle())  // this is the problem
console.log(p.toggle())

在第二层次的思考中,这实际上可能会变得更好,例如;

class Parent {
    #hidden;

    constructor() {
        this.#hidden = false;
    }

    get hidden() {
        return this.#hidden;
    }

    toggle() {
        this.#hidden = !this.#hidden;
        console.log("Changed",receiver) => target[prop].bind(target)});
console.log(p.toggle())
console.log(proxy.toggle())  // this is the problem
console.log(p.toggle());

这清楚地告诉您 Proxy 对象不是实例本身。您应该明确 bind 传递的函数属性(或原型方法),这些属性可以访问 Classtarget 的“private”属性,以便使它们正常运行。