[Proxies] Refactoring prototype climbing in the spec

Tom Van Cutsem tomvc.be at gmail.com
Sun Nov 13 08:42:43 PST 2011

2011/11/10 Allen Wirfs-Brock <allen at wirfs-brock.com>

> On Nov 10, 2011, at 9:03 AM, Tom Van Cutsem wrote:
> Regarding property deletion: if an object is implemented as a proxy, and
> you would want to delete a property from that object, I'm not sure why you
> would want to circumvent triggering the delete trap?
> In
> http://wiki.ecmascript.org/doku.php?id=strawman:object_model_reformation I'm
> exploring explicitly distinguishing the semants of obj.propName and
> obj[expression] such that an object can explicitly define its
> obj[expression] behaviors without using a Proxy (note that currently  by
> the time a Proxy is called, the distinction is already lost so even if you
> wanted to use a Proxy for this purpose they may not be adequate).  In many
> cases (including the default), the object-specific  "handler" for
> obj[expression] will delegate back to Object.getProperty/Object.setProperty
> (either directly or via a super call).  But note that it can do so via
> direct [ ] syntax, as that would loop.  This can actually all be modeled
> with a new Reference variant within the spec. that distinguishes .
> references from [ ] references.
> However, I also need to account for the delete operator and distinguish
> delete obj.propName from delete obj[expression] .  This can be taken care
> of by the same reference extension (essentially References needs a delete
> "method").  A handler for deleting obj[expression]] may also want to
> delegate back to normal property deletion and for the same reason as for
>  obj[exper]  get/set handlers it can't directly use the syntactic form.
>  Instead it needs to call an Object.deleteProperty function.


> BTW, rather than adding these methods to Object, I'm beginning to think it
> might be better to import them from a built-in reflection module.

Indeed. Perhaps that built-in reflection module could be the same module
that defines the default forwarding handler. Your note that we need an
Object.deleteProperty method apart from the built-in syntax reminded me
that the default forwarding handler for proxies is really all about
providing method-based alternatives to things that can currently only be
achieved through built-in syntax.

In the direct proxies proposal, I proposed defining a Proxy.forward object
that enables forwarding all operations interceptable by proxies to another
object. A prototype implementation of it exists here:

If you observe closely, you'll note that the default forwarding API is
really the "dual" of the Proxy API.
Proxies uniformly turn all sorts of operations on objects (whether
triggered through syntax or built-in methods) into trap invocations, for
Object.getOwnPropertyDescriptor(proxy, name) =>
handler.getOwnPropertyDescriptor(name, target)
name in proxy => handler.has(name, target)

The default forwarding API turns these trap invocations back into the
original operations:
Proxy.forward.getOwnPropertyDescriptor(name, target) =>
Object.getOwnPropertyDescriptor(target, name);
Proxy.forward.has(name, target) => name in target

That's why the forwarding API is crucial for double lifting to work: if a
handler is itself a proxy, then to intercept handler[trapName], we need to
be able to generically forward the operation, which is made possible by
writing Proxy.forward[trapName].

I proposed the name "Proxy.forward" since that looks very natural when
writing handler code that simply wants to "augment" the default behavior:

funtion makeChangeLogger(target) {
  return Proxy.for(target, {
    set: function(receiver, name, value, target) {
      log('property '+name+' on '+target+' set to '+value);
      return Proxy.forward.set(receiver, name, value, target);

However, it now seems to me that the methods encapsulated by Proxy.forward
can be more generally useful for arbitrary meta-programming, not just for
forwarding in the case of proxies.

I see an opportunity here to kill two birds with one stone: we can
introduce a reflection module (assume it's called "Reflect" for now) that
contains the Proxy.forward methods. That would:
a) obviate the need for Object.deleteProperty (now named Reflect.delete),
Object.getProperty (Reflect.get) and Object.setProperty (Reflect.set).
b) obviate the need for Proxy.forward (the above code snippet would call
Reflect.set instead, which is even shorter)

The design guideline for this Reflect module would be that the methods it
exposes have _exactly_ the same name + parameter list as the Proxy API trap
names. Not only does this retain consistency, it also enables easy double
lifting (or perhaps other design patterns that want to treat operations on
objects / Proxy traps generically).

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20111113/f470dc44/attachment.html>

More information about the es-discuss mailing list