> 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 <[email protected]> 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 <[email protected]> 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 >> <[email protected]> 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 >>> <[email protected]> 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 >>>> <[email protected]> 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: [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]> >>>>> 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]> On Behalf Of Laurie >>>>> Harper >>>>> Sent: Friday, June 5, 2020 12:21 AM >>>>> To: [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] >>>>> https://mail.mozilla.org/listinfo/es-discuss >>>>> >>>>> >>> >>> _______________________________________________ >>> es-discuss mailing list >>> [email protected] >>> https://mail.mozilla.org/listinfo/es-discuss > > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

