On Sep 4, 8:15 am, "T.J. Crowder" <t...@crowdersoftware.com> wrote:
> Hi all,
> I've come up with a new way of handling $super (which I can't imagine
> is unique, it's just new to me) which is markedly more efficient than
> our current $super on all of the browsers I've tried it on.  I'm
> wondering whether we should consider it for Prototype 2.

Our current solution is clearly one of the slowest inheritance
implementations out there. You get very nice sugar for a very high
price :) No surprises there. I think P2 should definitely employ a
simpler approach (either fully replacing old one or just as a separate

It's worth mentioning that one of the warts of current solution is
that it relies on function decompilation - something that's known to
cause problems [1][2] and is better avoided (fwiw, ES committee didn't
have enough time to handle this "issue" so ES5 specifies
`Function.prototype.toString` to return the very same *implementation-
dependent* function representation)

> Executive summary:
> Pros -
> * Avoids calling toString on subclass methods when defining classes.
> * Avoids creating closures for each $super-enabled subclass method.
> * Avoids creating an on-the-fly function on every call to a $super-
> enabled method.
> * Avoids at least four extra calls when $super-enabled methods are
> called.
> * Consequently reduces the time to call methods that use $super, from
> markedly (1.6X as fast) to dramatically (10X as fast) depending on
> browser and choices the subclass author makes.
> * Theoretically reduces memory footprint (no added functions/
> closures).
> * Minifiers (like packer3) that change arg names don't break things by
> renaming $super.
> * (Subjective) Simpler code in Class.create for contribs to
> understand.
> Cons -
> * Breaks backward compatibility.
> * Syntax for calling the $super method is slightly more complex.

  * Relies on `arguments.callee` with all the consequences.

To explain consequences:

`arguments.callee` is considered one of the warts of ES3. Besides
certain security concerns, the bad part of it is that it requires
creation of `arguments` object (when entering execution context). This
creation is rather expensive and results in both - memory and perf.
hits. Some of the modern implementations optimize in such way that
they only create `arguments` object when it statically occurs in a
function body (or when there's a chance of dynamic evaluation, such as
`eval`, `setTimeout` occurrence, etc.). Others create `arguments`
object on first resolution of `arguments` identifier, etc.

You can easily see it by yourself. Create 2 identical functions, one
of which would use `arguments.callee` and another - plain identifier;
then look at a difference.

ES5 also introduces strict mode. When in strict mode,
`argument.callee` throws TypeError on access. IIRC, Caja (secure
subset of ES) and some of its variations (Cajita, Valija), emulate ES5-
strict behavior and also throw error. If something in Prototype were
to rely on `arguments.callee`, it would effectively prevent running it
in strict mode or cajoling it.

[snip current approach explanation]

> Here's how the new approach works:
> 1. At class definition time, Class.create detects that the subclass
> overrides the superclass's method (this is a trivial property check)
> and, if so, stores a reference to the super's function as a property
> called $super on the sub's function object.
> 2. When a method is called, there is no indirection, the call goes
> direct to the subclass's defined method.  The subclass can then call
> the super's version via arguments.callee.$super.
> The unaided syntax subclass methods would use to call the super's
> version is complicated and error-prone (to the novice, anyway):
>     nifty: function(foo, bar) {
>         arguments.callee.$super.call(this, foo);
>     }
> ...and so this solution envisions a helper "callSuper" method (name
> TBD) authors may choose to use instead (at the cost of an extra
> function call):
>     nifty: function(foo, bar) {
>         this.callSuper(arguments, foo);
>     }
> You can see why I think a helper may be useful; that's quite a lot
> simpler.  Note that the first argument is always 'arguments' and does
> *not* mean #callSuper is passing all of the arguments to the super
> function; you follow it with the arguments you want to pass.  We can
> muck about with the API on the helper, and of course class authors can
> create their own for their own needs.

I usually use a similar approach instead of `$super`, which I first
saw mentioned by Tobie some time ago:

Class.Methods.callSuper = function(methodName) {
  var fn = this.constructor.superclass.prototype[methodName];
  return (arguments.length > 1)
    ? fn.apply(this, _slice.call(arguments, 1))
    : fn.call(this);


initialize: function(options) {
  this.callSuper('initialize', options);

One huge downside to this is that there's now repetition of method
name. If I change method name, I also have to remember to change first
argument of `callSuper` somewhere in the function body. The good thing
is that this approach is very efficient. There's also occurrence of
`arguments` there, but not `arguments.callee`.

[snip tests]

> Using the complicated syntax, that's a full order of magnitude *or
> better* on Chrome (but let's face it, Chrome is already so much faster
> than everything else it's less important than it would otherwise be),
> and Firefox sees ~5X improvement.  With the syntax I would expect most
> class authors to use, the benefit is less but still marked.
> Call overhead isn't sexy, but does anyone else think this is worth
> looking at more closely?

Definitely :)


[1] http://thinkweb2.com/projects/prototype/detecting-built-in-host-methods/
[2] http://thinkweb2.com/projects/prototype/those-tricky-functions/
You received this message because you are subscribed to the Google Groups 
"Prototype: Core" group.
To post to this group, send email to prototype-core@googlegroups.com
To unsubscribe from this group, send email to 
For more options, visit this group at 

Reply via email to