<div dir="ltr"><div>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.</div><div><br></div><div>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.</div><div><br></div><div>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.</div><div><br></div><div>Spec-wise, I think the only significant change is that <a href="http://www.ecma-international.org/ecma-262/6.0/#sec-hasproperty">7.3.10 HasProperty</a>, 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.</div><div><br></div><div>Cheers,</div><div>Tom</div></div><div class="gmail_extra"><br><div class="gmail_quote">2016-03-17 11:46 GMT+01:00 Michael Theriot <span dir="ltr"><<a href="mailto:michael.lee.theriot@gmail.com" target="_blank">michael.lee.theriot@gmail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>I feel like it should, or I am misunderstanding something fundamental. I made a basic scenario to explain:</div><div><br></div><div>```js</div><div>var arrays = new WeakMap();</div><div><br></div><div>function ArrayView(array) {</div><div>  arrays.set(this, array);</div><div><br></div><div>  return new Proxy(this, {</div><div>    set: (target, property, value) => (arrays.has(this) && property in arrays.get(this))  ? arrays.get(this)[property] = value : target[property] = value,</div><div>    get: (target, property)        => (arrays.has(this) && property in arrays.get(this))  ? arrays.get(this)[property]         : target[property],</div><div>    has: (target, property)        => (arrays.has(this) && property in arrays.get(this)) || property in target</div><div>  });</div><div>}</div><div><br></div><div>ArrayView.prototype = Object.create(Array.prototype, {</div><div>  arrayLength: {</div><div>    get() {</div><div>      return arrays.get(this).length;</div><div>    }</div><div>  }</div><div>});</div><div>```</div><div><br></div><div>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.</div><div><br></div><div>This could be simplified by putting the proxy on the prototype chain to reduce overhead and actually return a genuine `ArrayView` object instead:</div><div><br></div><div>```js</div><div>var arrays = new WeakMap();</div><div><br></div><div>function ArrayView2(array) {</div><div>  arrays.set(this, array);</div><div>}</div><div><br></div><div>var protoLayer = Object.create(Array.prototype, {</div><div>  arrayLength: {</div><div>    get() {</div><div>      return arrays.get(this).length;</div><div>    }</div><div>  }</div><div>});</div><div><br></div><div>ArrayView2.prototype = new Proxy(protoLayer, {</div><div>  set: (target, property, value, receiver) => (arrays.has(receiver) && property in arrays.get(receiver))  ? arrays.get(receiver)[property] = value : Reflect.set(target, property, value, receiver),</div><div>  get: (target, property, receiver)        => (arrays.has(receiver) && property in arrays.get(receiver))  ? arrays.get(receiver)[property]         : Reflect.get(target, property, receiver),</div><div>  has: (target, property)                  => (arrays.has(target)   && property in arrays.get(target))   || Reflect.has(target, property)</div><div>});</div><div>```</div><div><br></div><div>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.</div><div><br></div><div>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.</div><div><br></div><div>```js</div><div>var arr = [0, 1];</div><div><br></div><div>var a = new ArrayView(arr);</div><div>a.arrayLength; // 2</div><div>'arrayLength' in a; // true</div><div>'0' in a; // true</div><div>'1' in a; // true</div><div>'2' in a; // false</div><div><br></div><div>var b = new ArrayView2(arr);</div><div>b.arrayLength; // 2</div><div>'arrayLength' in b; // true</div><div>'0' in b; // false</div><div>'1' in b; // false</div><div>'2' in b; // false</div><div>```</div><div><br></div><div>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.</div><div><br></div><div>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.</div></div>
<br>_______________________________________________<br>
es-discuss mailing list<br>
<a href="mailto:es-discuss@mozilla.org">es-discuss@mozilla.org</a><br>
<a href="https://mail.mozilla.org/listinfo/es-discuss" rel="noreferrer" target="_blank">https://mail.mozilla.org/listinfo/es-discuss</a><br>
<br></blockquote></div><br></div>