Gotcha. So basically that tells the consumer "No, you can't use Proxies". What I want to tell them is "Go ahead, use Proxies to your heart's content", but I don't want to tell them to write all the overly-complicated code required for their Proxies to work properly as soon as I introduce a single private field within my class.
#!/JoePea On Mon, Jul 27, 2020 at 3:33 PM Jordan Harband <ljh...@gmail.com> wrote: > > oh oops, i meant `if (o !== this)` not `if (o.foo !== this)` :-) > > On Mon, Jul 27, 2020 at 3:06 PM #!/JoePea <j...@trusktr.io> wrote: >> >> > `o.foo(); // works` >> >> I think there is something missing from that example as that line >> throws before it can get to the `new Proxy` line. >> >> #!/JoePea >> >> On Wed, Jul 15, 2020 at 10:47 PM Jordan Harband <ljh...@gmail.com> wrote: >> > >> > So can: >> > ```jsx >> > const o = { foo() { if (o.foo !== this) { throw 'detected'; } } }; >> > o.foo(); // works >> > new Proxy(o, {}).foo(); // throws >> > ``` >> > >> > (as would a class that used a closed-over WeakMap for each "private field") >> > >> > Private fields do not introduce any new hazards here. >> > >> > On Tue, Jul 14, 2020 at 8:18 PM #!/JoePea <j...@trusktr.io> wrote: >> >> >> >> > private members (safely) allow classes with internal slots. >> >> >> >> I'd say that they aren't safe, if they can break 3rd-party code on the >> >> external public side. >> >> >> >> #!/JoePea >> >> >> >> On Sun, Jul 12, 2020 at 3:09 PM Michael Theriot >> >> <michael.lee.ther...@gmail.com> wrote: >> >> > >> >> > I assume OP wants to use proxies and private members together. They are >> >> > not designed to be compatible. >> >> > >> >> > Proxies and private members are a UX goal primarily for developers. >> >> > Proxies easily allow observation of another object or creation of >> >> > exotic objects (e.g. Array), and private members (safely) allow classes >> >> > with internal slots. Since they cannot be used together the issue >> >> > exists, and the hack circumvents this by reimplementing private in a >> >> > way that does not require private fields. >> >> > >> >> > On Sun, Jul 12, 2020 at 4:45 PM kai zhu <kaizhu...@gmail.com> wrote: >> >> >> >> >> >> as product-developer, can i ask what ux-objective you ultimately want >> >> >> achieved? >> >> >> >> >> >> ```js >> >> >> const sub = new Sub() >> >> >> >> >> >> // i'm a noob on proxies. what is this thing (with >> >> >> proxied-private-fields) ultimately used for? >> >> >> const proxy = new Proxy(sub, ...) >> >> >> ``` >> >> >> >> >> >> On Sun, Jul 12, 2020 at 4:34 PM Michael Theriot >> >> >> <michael.lee.ther...@gmail.com> wrote: >> >> >>> >> >> >>> This does require you to have both the key and the weakmap though, so >> >> >>> it actually does succeed in hiding the data so long as the weakmap is >> >> >>> out of scope. I guess the issue I can foresee is that the key could >> >> >>> be modified after the object is created. >> >> >>> >> >> >>> e.g. >> >> >>> ```js >> >> >>> var a = new A(); >> >> >>> var key = Object.getOwnPropertySymbols(a)[0]; >> >> >>> delete a[key]; >> >> >>> a.hidden; // throws >> >> >>> ``` >> >> >>> >> >> >>> That itself can be guarded by just making the key undeletable. So, I >> >> >>> guess this solution could work depending what your goals are? >> >> >>> >> >> >>> On Sun, Jul 12, 2020 at 4:21 PM Michael Theriot >> >> >>> <michael.lee.ther...@gmail.com> wrote: >> >> >>>> >> >> >>>> It nearly works, but the issue is that the key will be leaked by >> >> >>>> `Object.getOwnPropertySymbols(new A())`, so it's not truly private. >> >> >>>> >> >> >>>> There have been ideas proposing "private symbols" but I am not >> >> >>>> familiar with their issues, and I would guess with Class Fields they >> >> >>>> are unlikely to materialize anyway. >> >> >>>> >> >> >>>> On Sun, Jul 12, 2020 at 2:19 PM François REMY >> >> >>>> <francois.remy....@outlook.com> wrote: >> >> >>>>> >> >> >>>>> 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 >> >> >>>>> Sent: Sunday, July 12, 2020 20:59 >> >> >>>>> To: Michael Haufe >> >> >>>>> Cc: 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> 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> On Behalf Of >> >> >>>>> Laurie Harper >> >> >>>>> Sent: Friday, June 5, 2020 12:21 AM >> >> >>>>> To: 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 >> >> >>>>> https://mail.mozilla.org/listinfo/es-discuss >> >> >>>>> >> >> >>>>> >> >> >>> >> >> >>> _______________________________________________ >> >> >>> es-discuss mailing list >> >> >>> 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 >> >> _______________________________________________ >> >> es-discuss mailing list >> >> 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