At the risk of pointing out the obvious:
```js
const privkey = Symbol();
const stores = new WeakMap();
class A {
[privkey] = {};
constructor() {
const priv = {};
priv.hidden = Math.random();
stores.set(this[privkey], priv);
}
get hidden() {
const priv = stores.get(this[privkey]);
return priv.hidden;
}
}
var as = [
new A(),
new Proxy(new A(),{}),
new Proxy(new A(),{}),
];
console.log(as.map(a=>a.hidden));
```
From: Michael Theriot<mailto:[email protected]>
Sent: Sunday, July 12, 2020 20:59
To: Michael Haufe<mailto:[email protected]>
Cc: [email protected]<mailto:[email protected]>
Subject: Re: Why does a JavaScript class getter for a private field fail using
a Proxy?
I experienced this issue prior to this proposal, using weakmaps for private
access.
e.g.
```js
const stores = new WeakMap();
class A {
constructor() {
const priv = {};
priv.hidden = 0;
stores.set(this, priv);
}
get hidden() {
const priv = stores.get(this);
return priv.hidden;
}
}
const a = new A();
console.log(a.hidden); // 0
const p = new Proxy(a, {});
console.log(p.hidden); // throws!
```
I found a workaround:
```js
const stores = new WeakMap();
class A {
constructor() {
const priv = {};
priv.hidden = 0;
stores.set(this, priv);
const p = new Proxy(this, {});
stores.set(p, priv); // set proxy to map to the same private store
return p;
}
get hidden() {
const priv = stores.get(this); // the original instance and proxy both map
to the same private store now
return priv.hidden;
}
}
const a = new A();
console.log(a.hidden);
```
Not ideal, and only works if you provide the proxy in the first place (e.g.
making exotic JS objects). But, not necessarily a new issue with proxies,
either.
On Fri, Jun 5, 2020 at 12:29 AM Michael Haufe
<[email protected]<mailto:[email protected]>> wrote:
This is a known issue and very painful for me as well. You can see a long ugly
discussion here:
<https://github.com/tc39/proposal-class-fields/issues/106>
I suggest the following guide to assist you:
<https://javascript.info/proxy#proxy-limitations>
Another possible approach is to have your classes extend a proxy:
<https://github.com/tc39/proposal-class-fields/issues/106#issuecomment-397484713>
From: es-discuss
<[email protected]<mailto:[email protected]>> On
Behalf Of Laurie Harper
Sent: Friday, June 5, 2020 12:21 AM
To: [email protected]<mailto:[email protected]>
Subject: Why does a JavaScript class getter for a private field fail using a
Proxy?
I can expose private class fields in JavaScript using getters, and those
getters work correctly when invoked on instances of a subclass. However, if I
then wrap the instance with a proxy the getter will throw a type error, even if
the proxy `get` hook uses `Reflect.get()`:
```
class Base {
_attrA
#_attrB
constructor() {
this._attrA = 100
this.#_attrB = 200
}
get A() { return this._attrA }
get B() { return this.#_attrB }
incrA() { this._attrA++ }
incrB() { this.#_attrB++ }
}
class Sub extends Base {}
const sub = new Sub()
const proxy = new Proxy(sub, {
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver)
return typeof value === 'function' ? value.bind(target) : value // (1)
}
})
console.log('sub.A', sub.A) // OK: -> 100
console.log('sub.B', sub.B) // OK: -> 200
sub.incrA() // OK
sub.incrB() // OK
console.log('sub.A', sub.A) // OK: -> 101
console.log('sub.B', sub.B) // OK: -> 201
console.log('proxy.A', proxy.A) // OK: -> 100
console.log('proxy.B', proxy.B) // TypeError: Cannot read private member
#_attrB from an object whose class did not declare it
proxy.incrA() // OK
proxy.incrB() // OK due to (1)
console.log('proxy.A', proxy.A) // OK: -> 100
console.log('proxy.B', proxy.B) // TypeError: Cannot read private member
#_attrB from an object whose class did not declare it
```
The call to `proxy.incrB()` works, because the proxy handler explicitly binds
function values to `target` on line (1). Without the `bind()` call, the
`proxy.incrB()` invocation would throw a `TypeError` like the getter invocation
does. That makes some sense: the result of the call to `Reflect.get()` is the
'unbound' function value of the property being retrieved, which must then be
bound to `target`; it would make more sense, though, if `this` binding was
applied by the [[Call]] operation on the result of the [[Get]] operation...
But there is no opportunity to 'bind' a getter before invoking it; as a result,
a proxied getter ends up receiving the wrong `this` binding, leading to the
inconsistency.
Is there any way to make this work correctly? The only approach I can think of
(which I haven't tried) would be to have the `get` hook walk up the prototype
chain, starting from `target`, calling `getOwnPropertyDescriptor()` and
checking for a getter method, and explicitly applying the getter with an
adjusted `this` binding. That sounds ludicrously cumbersome and brittle...
Is there a better way to get this working correctly?
--
Laurie
_______________________________________________
es-discuss mailing list
[email protected]<mailto:[email protected]>
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss