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
对象提供对 target
和 prop
的直接访问权限,因此您可以像直接从实例访问一样简单地访问私有数据。
我的方法是这样的;
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
传递的函数属性(或原型方法)可以访问Class
到target
的“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: () => // 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)的主要内容,如果未能解决你的问题,请参考以下文章