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

Michael Theriot michael.lee.theriot at gmail.com
Sun Apr 3 07:11:20 UTC 2016


That is good news then. I think I have the right expectations of proxies
now.

Sharing one handler is easy too. All you need to do is map both the `proxy`
and its `target` to the same data. `receiver` is actually the proxy but the
argument is no longer important here.

```js
var priv = new WeakMap();

var handler = {
  get: (target, property, receiver) => property in priv.get(target) ?
priv.get(target)[property] : target[property],
  set: (target, property, value, receiver) => property in
priv.get(target) ? priv.get(target)[property] = value : target[property] =
value,
  has: (target, property) => property in priv.get(target) || property in
target
};

function A() {
  let proxy = new Proxy(this, handler);
  let store = {secret: 4};
  priv.set(this, store).set(proxy, store);
  return proxy;
}

A.prototype.getSecret = function () {
  return priv.get(this).secret;
};

var a = new A();
a.getSecret(); // 4
a.secret; // 4
a.secret = 5;
a.secret; // 5
a.getSecret(); // 5
'secret' in a; // true
```

(sorry for any dupes, new to mailing lists...)

On Thu, Mar 17, 2016 at 5:46 AM, Michael Theriot <
michael.lee.theriot at gmail.com> wrote:

> 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.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20160403/d7278e9b/attachment.html>


More information about the es-discuss mailing list