Proxy handler.has() does not have a receiver argument?

Andrea Giammarchi andrea.giammarchi at gmail.com
Fri Mar 18 16:24:10 UTC 2016


Agreed with everybody else the `receiver` is always needed and `Proxy` on
the prototype makes way more sense than per instance.

Also the `getPrototypeOf` trap is really pointless right now

```js
function Yak() {}
Yak.prototype = new Proxy(Yak.prototype, {
  getPrototypeOf: (target) => console.log('lulz')
});

var yup = new Yak;
Object.getPrototypeOf(yup);
```

The `target` is actually the original `Yak.prototype` which is already the
`yup` prototype: useless trap if used in such way.

Being also unable to distinguish between `getOwnPropertyNames` vs `keys` is
a bit weird.

`Proxy` looks so close to be that powerful but these bits make it kinda
useless for most real-world cases I've been recently dealing with.

Thanks for any sort of improvement.

Regards




On Fri, Mar 18, 2016 at 1:54 PM, Michael Theriot <
michael.lee.theriot at gmail.com> wrote:

> I'm trying to make the proxy-as-a-prototype pattern work but I've just
> discovered the `ownKeys` trap is never called on traps on the prototype. So
> even if the `has` trap is allowed to see the `receiver`, and thus verify
> the properties "0", "1" exist, this pattern would fail to return the
> properties "0", "1" exist on an `Object.getOwnPropertyNames` call.
> Disappointing! I'd rather use a proxy on the prototype than create one for
> each instance but without a correct `ownKeys` return it just doesn't come
> full circle. Is there a trick to make this work or am I out of luck here? I
> can only think of actually defining the properties to make it work, which
> defeats the idea of using a proxy on the prototype to begin with.
>
> Regardless I agree that traps called on a prototype chain should always
> receive the `receiver` as an argument. I think the only trap other than
> `set`, `get`, and `has` that can do this is the `getPrototypeOf` trap
> (currently does not have a `receiver`) when the `instanceof` check needs to
> climb the prototype chain.
>
> On Thu, Mar 17, 2016 at 6:29 PM, Tom Van Cutsem <tomvc.be at gmail.com>
> wrote:
>
>> The rationale for not having a `receiver` argument to `has` is that the
>> value produced by the "in" operator is not normally dependent on the
>> receiver. This is in contrast with `get` and `set` which may find an
>> accessor up the proto chain that needs to run with a `this` bound to the
>> receiver.
>>
>> That said, I follow your line of reasoning and it is true that `has`,
>> `get` and `set` are the three traps that can be called on a
>> proxy-used-as-prototype (now that `enumerate` is considered deprecated), so
>> it would be consistent to allow all of them to  refer back to the original
>> receiver. This enables the general pattern that you illustrate.
>>
>> As you note, the weirdness of this is apparent because it doesn't
>> normally make sense to pass a `receiver` argument to Reflect.has().
>> However, if `receiver` would be made visible in a Proxy handler's `has`
>> trap, then `Reflect.has` should nevertheless be likewise extended so that
>> one can faithfully forward the `receiver` argument.
>>
>> Spec-wise, I think the only significant change is that 7.3.10 HasProperty
>> <http://www.ecma-international.org/ecma-262/6.0/#sec-hasproperty>, step
>> 3 must be changed to `O.[[HasProperty]](P, O)` and all [[HasProperty]]
>> internal methods must likewise be extended with an extra argument (which
>> they ignore). Only the Proxy implementation in 9.5.7 would then actually
>> refer to that argument.
>>
>> Cheers,
>> Tom
>>
>> 2016-03-17 11:46 GMT+01:00 Michael Theriot <michael.lee.theriot at gmail.com
>> >:
>>
>>> I feel like it should, or I am misunderstanding something fundamental. I
>>> made a basic scenario to explain:
>>>
>>> ```js
>>> var arrays = new WeakMap();
>>>
>>> function ArrayView(array) {
>>>   arrays.set(this, array);
>>>
>>>   return new Proxy(this, {
>>>     set: (target, property, value) => (arrays.has(this) && property in
>>> arrays.get(this))  ? arrays.get(this)[property] = value : target[property]
>>> = value,
>>>     get: (target, property)        => (arrays.has(this) && property in
>>> arrays.get(this))  ? arrays.get(this)[property]         : target[property],
>>>     has: (target, property)        => (arrays.has(this) && property in
>>> arrays.get(this)) || property in target
>>>   });
>>> }
>>>
>>> ArrayView.prototype = Object.create(Array.prototype, {
>>>   arrayLength: {
>>>     get() {
>>>       return arrays.get(this).length;
>>>     }
>>>   }
>>> });
>>> ```
>>>
>>> When `new ArrayView(somearray)` is called the reference to `somearray`
>>> is stored in the `arrays` weak map and a proxy is returned that allows you
>>> to manipulate indices on it, or fallback to the object for other properties.
>>>
>>> This could be simplified by putting the proxy on the prototype chain to
>>> reduce overhead and actually return a genuine `ArrayView` object instead:
>>>
>>> ```js
>>> var arrays = new WeakMap();
>>>
>>> function ArrayView2(array) {
>>>   arrays.set(this, array);
>>> }
>>>
>>> var protoLayer = Object.create(Array.prototype, {
>>>   arrayLength: {
>>>     get() {
>>>       return arrays.get(this).length;
>>>     }
>>>   }
>>> });
>>>
>>> ArrayView2.prototype = new Proxy(protoLayer, {
>>>   set: (target, property, value, receiver) => (arrays.has(receiver) &&
>>> property in arrays.get(receiver))  ? arrays.get(receiver)[property] = value
>>> : Reflect.set(target, property, value, receiver),
>>>   get: (target, property, receiver)        => (arrays.has(receiver) &&
>>> property in arrays.get(receiver))  ? arrays.get(receiver)[property]
>>> : Reflect.get(target, property, receiver),
>>>   has: (target, property)                  => (arrays.has(target)   &&
>>> property in arrays.get(target))   || Reflect.has(target, property)
>>> });
>>> ```
>>>
>>> Under this setup `target` refers to the protoLayer object which is
>>> useless here, but we can use the `receiver` argument in its place to access
>>> the weak map, and replace our set/get operations with
>>> Reflect.set/Reflect.get calls to the target (protoLayer) using a receiver
>>> (the instance) to pass the correct `this` value to the `arrayLength` getter
>>> and prevent infinite recursion.
>>>
>>> One problem - handler.has() lacks a receiver argument. So in this
>>> scenario when using the `in` operator it will always fail on array
>>> properties because we cannot check the weak map by passing in the instance.
>>>
>>> ```js
>>> var arr = [0, 1];
>>>
>>> var a = new ArrayView(arr);
>>> a.arrayLength; // 2
>>> 'arrayLength' in a; // true
>>> '0' in a; // true
>>> '1' in a; // true
>>> '2' in a; // false
>>>
>>> var b = new ArrayView2(arr);
>>> b.arrayLength; // 2
>>> 'arrayLength' in b; // true
>>> '0' in b; // false
>>> '1' in b; // false
>>> '2' in b; // false
>>> ```
>>>
>>> Without a receiver argument on handler.has(), it is practically useless
>>> for proxies used as a prototype. You can't reference the instance calling
>>> it and your target is simply the parent prototype.
>>>
>>> Is there a reason the handler.has() trap should not obtain the receiver
>>> when used on the prototype chain? I can understand why Reflect.has()
>>> wouldn't have a receiver argument (that wouldn't make sense) but this seems
>>> like a legitimate use for it. Otherwise I don't see a reason to use the
>>> handler.has() trap at all on prototype proxies except for bizarre behaviors
>>> that have nothing to do with the instance. It will always have the same
>>> behavior across all instances since you can't differentiate them.
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>>
>>>
>>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160318/c2361eb0/attachment-0001.html>


More information about the es-discuss mailing list