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

Posted

技术标签:

【中文标题】ES6 代理类,访问私有属性(无法从类未声明的对象中读取私有成员#hidden)【英文标题】:ES6 proxied class, access private property (Cannot read private member #hidden from an object whose class did not declare it) 【发布时间】:2021-07-28 16:41:41 【问题描述】:

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

/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(..., 
    get: (target, prop, receiver) => 

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

        return target[prop];

    
);

但这似乎很不干净/错误。

【问题讨论】:

出于好奇,使用代理的动机是什么?也许还有其他选择。 这就是为什么私有字段是个坏主意。 @loganfsmyth 很抱歉,我的回答很长一段时间:我想将对象“合并”在一起。 2 具有不同方法/属性的单独对象应该可以通过一个作为“后备”访问。它的属性x 在obj1 上不存在,试试obj2。为此,代理应该“透明”地工作。我想我用... instanceof Function/.bind(...)进行检查 【参考方案1】:

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

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, prop, 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:', p.toggle());
console.log('p2 toggle:', p2.toggle());

【讨论】:

"注意:你必须使用newTarget of handler.construct" - OP 根本不使用construct 陷阱吗?这工作正常。在您的示例中,您的处理程序只使用完全相同的参数分派到Reflect.construct,这与根本不捕获construct 相同。 哦,我的错。你是对的,谢谢指出【参考方案2】:

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,prop,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", this.#hidden)
        return this.#hidden;
    




var p = new Parent();
var proxy = new Proxy(p,get: (target,prop,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”属性,以使它们正常运行。 p>

【讨论】:

@Bergi。是的,但这没什么大不了的。当我们对target[prop] 进行功能测试时,它可以简单地工作。 所以请这样做。【参考方案3】:

这是不可能的,一旦你做了一个代理,阅读它说的错误消息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

【讨论】:

但是代理以 1:1 的比例返回对象实例,this 的上下文没有改变,并且代理 obcjet 上的任何查找/访问都被直接转发到原始实例。 问题是您试图从代理访问私有实例,而不是从类内部。 get: () =&gt; // here we are accessing from the proxy not from the class itself 所以这是预期的行为 有道理,但为什么我可以记录属性console.log(p.hidden, proxy.hidden)?这行得通......我预计这也会失败。 好吧,因为您正在调用 public getter get hidden 这不是私有的。如果你尝试console.log(p.#hidden),我相信你会得到一个错误 没错,但toggle() 也不是私人的

以上是关于ES6 代理类,访问私有属性(无法从类未声明的对象中读取私有成员#hidden)的主要内容,如果未能解决你的问题,请参考以下文章

如何从类内实例化的对象访问私有变量

如何从类函数内部访问对象属性[重复]

在 C# 中从类外部访问私有构造函数

可以从类外部访问私有 var

Python面向对象-方法

C++ 析构函数:无法访问类中声明的私有成员