Michael Theriot (2016-03-18T21:45:41.000Z)
michael.lee.theriot at gmail.com (2016-03-18T21:49:04.209Z)
> Michael’s preferred approach also introduces observable irregularity into > the standard JS inheritance model for ordinary objects. > Consider an object created using Michael’s preferred approach: > ```js > var arr = [0, 1]; > console.log(Reflect.has(arr,”0”)); //true, arr has “0” as an own property > var subArr = Object.create(arr); > console.log(Reflect.has(subArr,”0”)); //true, all unshadowed properties > of proto visible from ordinary objects > var b = new ArrayView2(arr); > console.log(Reflect.has(b,”0”)); //true, prototype proxy makes array > elements visible as if properties of b > var subB= Object.create(b); > console.log(Reflect.has(subB,”0”)); //false, some unshadowed properties > of proto is not visible from subB > ``` I think this relates to the original concern; if you could pass the receiver this could be resolved. That still leaves `getOwnPropertyNames` reporting wrong values though and I see no foreseeable way to resolve that without returning an actual proxy itself. The reason I'm trying this approach is because I read on the MDN that when used in the prototype a `receiver` argument is passed that references the instance, so I assumed this was the intent behind it. The only other explanation I could think of is that proxies have a receiver to mimic the `Reflect.set`/`Reflect.get` methods which need a receiver for getters/setters to work properly, not so you can use them on the prototype chain. The other case I would make is every instance would have an identical proxy, and it just makes sense to put that on the prototype for the same reasons you put shared methods/properties there. > Note that we are not really talking about a new capability here. Michael’s > first design shows that ES proxies already have the capability to implement > the object level semantics he desires. To be fair I had several obstacles with inheritance using the first version. ```js var wm1 = new WeakMap(); function A() { wm1.set(this, {}); return new Proxy(this, {}); } var wm2 = new WeakMap(); function B() { A.call(this); wm2.set(this, {}); return new Proxy(this, {}); } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // false wm2.has(b); // true ``` As you can see storing a reference to `this` can't work anymore, since we actually return a proxy. You can try to work around this... ```js var wm1 = new WeakMap(); function A() { let self = this; if(new.target === A) { self = new Proxy(this, {}); } wm1.set(self, {}); return self; } var wm2 = new WeakMap(); function B() { let self = this; if(new.target === B) { self = new Proxy(this, {}); } A.call(self); wm2.set(self, {}); return self; } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // true wm2.has(b); // true ``` But then problems arise because the new proxy doesn't go through the old proxy. So anything guaranteed by A()'s proxy is not guaranteed by B()'s proxy. ```js var wm1 = new WeakMap(); function A() { let self = this; if(new.target === A) { self = new Proxy(this, { get: (target, property, receiver) => property === 'bacon' || target[property] }); } wm1.set(self, {}); return self; } var wm2 = new WeakMap(); function B() { let self = this; if(new.target === B) { self = new Proxy(this, { get: (target, property, receiver) => property === 'ham' || target[property] }); } A.call(self); wm2.set(self, {}); return self; } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // true wm2.has(b); // true a.bacon; // true a.ham; // undefined b.bacon; // undefined b.ham; // true ``` (I'm open to solutions on this particular case... One that doesn't require me to leak the handler of the A proxy) Ultimately I can actually achieve both what I want with the ArrayView example and inheritance by using a **lot** of `defineProperty` calls on `this` in the constructor, but performance is a disaster as you might expect.
michael.lee.theriot at gmail.com (2016-03-18T21:47:51.517Z)
> Michael’s preferred approach also introduces observable irregularity into > the standard JS inheritance model for ordinary objects. > Consider an object created using Michael’s preferred approach: > ```js > var arr = [0, 1]; > console.log(Reflect.has(arr,”0”)); //true, arr has “0” as an own property > var subArr = Object.create(arr); > console.log(Reflect.has(subArr,”0”)); //true, all unshadowed properties > of proto visible from ordinary objects > var b = new ArrayView2(arr); > console.log(Reflect.has(b,”0”)); //true, prototype proxy makes array > elements visible as if properties of b > var subB= Object.create(b); > console.log(Reflect.has(subB,”0”)); //false, some unshadowed properties > of proto is not visible from subB > ``` I think this relates to the original concern; if you could pass the receiver this could be resolved. That still leaves `getOwnPropertyNames` reporting wrong values though and I see no foreseeable way to resolve that without returning an actual proxy itself. The reason I'm trying this approach is because I read on the MDN that when used in the prototype a `receiver` argument is passed that references the instance, so I assumed this was the intent behind it. The only other explanation I could think of is that proxies have a receiver to mimic the `Reflect.set`/`Reflect.get` methods which need a receiver for getters/setters to work properly, not so you can use them on the prototype chain. The other case I would make is every instance would have an identical proxy, and it just makes sense to put that on the prototype for the same reasons you put shared methods/properties there. > Note that we are not really talking about a new capability here. Michael’s > first design shows that ES proxies already have the capability to implement > the object level semantics he desires. To be fair I had several obstacles with inheritance using the first version. ```js var wm1 = new WeakMap(); function A() { wm1.set(this, {}); return new Proxy(this, {}); } var wm2 = new WeakMap(); function B() { A.call(this); wm2.set(this, {}); return new Proxy(this, {}); } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // false wm2.has(b); // true ``` As you can see storing a reference to `this` can't work anymore, since we actually return a proxy. You can try to work around this... ```js var wm1 = new WeakMap(); function A() { let self = this; if(new.target === A) { self = new Proxy(this, {}); } wm1.set(self, {}); return self; } var wm2 = new WeakMap(); function B() { let self = this; if(new.target === B) { self = new Proxy(this, {}); } A.call(self); wm2.set(self, {}); return self; } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // true wm2.has(b); // true ``` But then problems arise because the new proxy doesn't go through the old proxy. So anything guaranteed by A()'s proxy is not guaranteed by B()'s proxy. ```js var wm1 = new WeakMap(); function A() { let self = this; if(new.target === A) { self = new Proxy(this, { get: (target, property, receiver) => property === 'bacon' || target[property] }); } wm1.set(self, {}); return self; } var wm2 = new WeakMap(); function B() { let self = this; if(new.target === B) { self = new Proxy(this, { get: (target, property, receiver) => property === 'ham' || target[property] }); } A.call(self); wm2.set(self, {}); return self; } var a = new A(); var b = new B(); wm1.has(a); // true wm2.has(a); // false wm1.has(b); // true wm2.has(b); // true a.bacon; // true a.ham; // undefined b.bacon; // undefined b.ham; // true ``` (I'm open to solutions on this particular case... One that doesn't require me to leak the handler of the A proxy) Ultimately I can actually achieve both what I want with the ArrayView example and inheritance by using a **lot** of `defineProperty` calls on `this` in the constructor, but performance is a disaster as you might expect.