Notification proxies (Was: possible excessive proxy invariants for Object.keys/etc??)

David Bruant bruant.d at
Mon Dec 3 14:27:30 PST 2012

Le 03/12/2012 16:38, Mark S. Miller a écrit :
> What eternal[1] invariant does this bypass?
> [1]
Apparently none... Yet, additionally to the last case I showed, there is 

     var p = new Proxy({a:1}, {
         isExtensible: function(target, action){
             var v = action();

     // real life-like code?
         Object.defineProperty(target, 'b', {value: 37}); // throws

I agree it's not an eternal invariant, but it's quite surprising.
[cc'ing Tom to make sure he reads this part]
Arguably, the isExtensible, seal and freeze trap could have no "action" 
and just forward to the target. That's what the current invariant 
enforcement suggests anyway ("Invariant check: check whether the boolean 
trap result is equal to isFrozen(target), isSealed(target) or 
This applies to current proxies actually. Maybe their return value could 
just be ignored. Trap authors have all the information they need with 
the trap arguments to decide whether they want to throw. For the rest, 
the operation can just be forwarded to the target (which it has to for 
invariant check already).

Proxies-with-action have this weird taste of "one-move ahead"; like if 
they could run what they're expected and then play a bit more of the 
game before actually showing their before-last move. It makes sense they 
do not violate eternal invariants

Although not rigorously necessary when it comes to the very minimalistic 
eternal invariants, the current proxies provides some guarantees by 
design which are nice both for whoever writes handlers and whoever 
manipulates proxies.

Unrelated, but I think that custom property descriptor attributes are 
lost with action-proxies. I'm not sure yet what is a good way to recover 


> On Mon, Dec 3, 2012 at 3:33 AM, David Bruant <bruant.d at> wrote:
>> Le 03/12/2012 00:06, David Bruant a écrit :
>>>> The call to action performs
>>>> the original operation on target and remembers the result. After the
>>>> trap returns, the proxy returns the remembered result of action.
>>>      target = {a:1};
>>>      var p = new Proxy(target, {
>>>          get: function(target, name, action){
>>>              var v = action();
>>>              target[name] = 2;
>>>          }
>>>      })
>>>      p.a; // ?
>>> If p.a is 1 because the call to "action" remembered the "1", then the
>>> target and the proxy look awkwardly desynchronized. To know what's being
>>> returned from the trap, one needs to remember when the action is being
>>> called which makes writing traps harder I feel. With the current design,
>>> looking at the return statements is enough.
>>> If p.a is 2, I don't understand the point of "action". Or at least, I
>>> don't see how it's better than just calling Reflect[trap].
>> I've found much MUCH worse:
>>      target = {a:1};
>>      var p = new Proxy(target, {
>>          get: function(target, name, action){
>>              var v = action();
>>              Object.defineProperty(target, name, {value:2, configurable:
>> false, writable:false})
>>          }
>>      })
>>      p.a; // 1 ?
>> In that case, the mechanism used to bypass invariant checking is a way to
>> bypass invariants entirely.
>> David
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at

More information about the es-discuss mailing list