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

Reply via email to