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:michael.lee.ther...@gmail.com> Sent: Sunday, July 12, 2020 20:59 To: Michael Haufe<mailto:t...@thenewobjective.com> Cc: es-discuss@mozilla.org<mailto:es-discuss@mozilla.org> 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 <t...@thenewobjective.com<mailto:t...@thenewobjective.com>> 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 <es-discuss-boun...@mozilla.org<mailto:es-discuss-boun...@mozilla.org>> On Behalf Of Laurie Harper Sent: Friday, June 5, 2020 12:21 AM To: es-discuss@mozilla.org<mailto:es-discuss@mozilla.org> 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 es-discuss@mozilla.org<mailto:es-discuss@mozilla.org> https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss