Why is there a $super-Parameter?

9 views
Skip to first unread message

Luke

unread,
Mar 18, 2011, 7:43:50 AM3/18/11
to prototype-s...@googlegroups.com
Hi,

I'm working on my bachelor-thesis and I'm trying to understand a few things. I'm wondering: Why is there the construct of the $super-parameter in Prototype's class-implementation? Is anything wrong with calling this.parent.myfunction()? Why did they implement that?

This is more of a theoretical question.. I just think there must be a reason for the $super thing..

Does anyone have an idea?

Thanks
Lukas

Richard Quadling

unread,
Mar 18, 2011, 8:45:49 AM3/18/11
to prototype-s...@googlegroups.com, Luke
> --
> You received this message because you are subscribed to the Google Groups
> "Prototype & script.aculo.us" group.
> To post to this group, send email to
> prototype-s...@googlegroups.com.
> To unsubscribe from this group, send email to
> prototype-scripta...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/prototype-scriptaculous?hl=en.
>

parent relates to the DOM which is an object orientated model.

Javascript classes are not OOP, but prototypes. As such "parent" is
not appropriate.

To simulate "parent" in PrototypeJS, $super was defined.

http://prototypejs.org/learn/class-inheritance

--
Richard Quadling
Twitter : EE : Zend
@RQuadling : e-e.com/M_248814.html : bit.ly/9O8vFY

Luke

unread,
Mar 18, 2011, 9:13:17 AM3/18/11
to prototype-s...@googlegroups.com, Luke
I'm sorry, what I meant was this.superclass In Prototype's class implementation you can see that if you provide a parent-class in prototype's Class.Create-Method, that class will be referenced as superclass in the class you are creating:

  function create() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();
    // ...
    klass.superclass = parent;
    // ...
    return klass;
  }

why not just use this.superclass.method in the subclass?

Ryan Gahl

unread,
Mar 18, 2011, 9:52:30 AM3/18/11
to prototype-s...@googlegroups.com
The answer is quite simple: because it's convenient.

Convenience is an interesting thing. You seem to be a logical type, postulating that "there must be something beyond just convenience here," because if it can be done without this construct, why add the construct, right? That takes the human part of the equation right of the picture though. The truth is that humans, even programmers, are irrational. We sometimes do things just because, or barring that, just because it achieves a certain stylistic property that pleases us.

In this case, I think it's both "just because" and "because it's pretty". At a minimum, it's convenient.




--

Luke

unread,
Mar 18, 2011, 10:21:31 AM3/18/11
to prototype-s...@googlegroups.com
Sounds logical ;)

No really, thx :)

T.J. Crowder

unread,
Mar 18, 2011, 10:30:08 AM3/18/11
to Prototype & script.aculo.us
Hi,

> why not just use this.superclass.method in the subclass?

If you mean:

this.superclass.method(arg);

...there are a couple of problems there. The first, which could easily
be fixed, is that the above would lose the meaning of `this` within
the call to the parent method. To preserve it, you'd need to do this:

this.superclass.method.call(this, arg);

But that won't work either, because there is no
`this.superclass.method` available (in fact, no `this.superclass`)
within the code of an instance's method: http://jsbin.com/uhitu5

The closest to `this.superclass.method` you can come is:

this.constructor.superclass.prototype.method.call(this, arg);

...which works (http://jsbin.com/uhitu5/3) but I think you'd agree is
fairly long-winded. :-) (It also assumes nothing writes to the
`constructor` property, which is *probably* a valid assumption, and
yet...)

Or you could constantly reiterate the name of your class:

ThisClass.superclass.prototype.method.call(this, arg);

...which also works (http://jsbin.com/uhitu5/2) but makes it a pain to
rename your class.

Or reiterate the name of your parent class:

ParentClass.prototype.method.call(this, arg);

...which also works but makes it a pain to rename the parent class or
rebase your class.

Regardless, compared with Prototype's magic

$super(arg);

...even the most terse of the above is verbose. :-)

Prototype's magical `$super` comes at a marked runtime cost -- not
that it usually matters -- and relies on unstandardized behavior
(function decompilation), which is probably more of a concern (as it's
known not to work on some mobile browsers). I did an alternate
mechanism that's more efficient and doesn't rely on function
decompilation, but at the cost of adding slightly to the complexity of
making the call:

method.$super.call(this, arg);

or with a helper method (which is added overhead):

this.callSuper(method, arg);

Details here, it may be useful reading for your thesis (or not):
http://blog.niftysnippets.org/2009/09/simple-efficient-supercalls-in.html

HTH,
--
T.J. Crowder
Independent Software Engineer
tj / crowder software / com
www / crowder software / com

Ryan Gahl

unread,
Mar 18, 2011, 10:39:20 AM3/18/11
to prototype-s...@googlegroups.com

On Fri, Mar 18, 2011 at 9:30 AM, T.J. Crowder <t...@crowdersoftware.com> wrote:
Prototype's magical `$super` comes at a marked runtime cost


Just want to point out that the "marked runtime cost" is only at class definition time, not instance creation nor method call time. So yeah, the performance thing is a non issue.

Luke

unread,
Mar 18, 2011, 11:01:03 AM3/18/11
to prototype-s...@googlegroups.com
Thanks a lot TJ that helps!

T.J. Crowder

unread,
Mar 18, 2011, 12:01:17 PM3/18/11
to Prototype & script.aculo.us
Hi Ryan,

> Just want to point out that the "marked runtime cost" is only at class
> definition time, not instance creation nor method call time.

Actually, it's every single time anything calls a method that has the `
$super` argument (whether the `$super` argument gets used or not).
Every single call, there are multiple overhead function calls and a
function *creation*. Here's what happens:

1. The call to your method is actually to a Prototype wrapper for your
method
2. The wrapper calls `Function#bind` (yes, every time)
3. `bind` calls `Array#slice`, because `bind` handles arguments
(although it doesn't in this case)
4. `bind` creates and returns a new function
5. The wrapper calls its internal `update` function to update the
arguments
6. The wrapper calls your actual method via `apply`

If you actually *use* `$super`, then there are about five extra
function calls involved before the parent method actually gets called
(but no new functions get created).

So the runtime costs are significant, not just at `Class.create` time
(decompiling every public function in your class to find out whether
it has a `$super` argument), but also on each and every call to a
method with a `$super` argument (several additional function calls,
including creating new function objects).

Probably *at least* 95% of the time you *don't care*, but still, I was
pretty shocked when I found out what it was doing, which is what lead
me to thinking if there was a better way. My mechanism doesn't do any
function decompilation, doesn't create any functions when your methods
are called, and introduces *no* extra calls at all [a call to one of
your methods goes straight to your method, and your call to the parent
method goes straight to the parent method (unless you use an optional
helper function)]. But the notation is a bit longer. ;-)

How much longer? If you use proper named functions, the call can be

method.$super.call(this, arg);

...compared with Prototype's:

$super(arg);

If you use anonymous functions (`method: function(arg) { ... }`), then
it's

this.method.$super.call(this, arg);

I don't use anonymous functions, so I get the shorter one.

-- T.J. :-)

T.J. Crowder

unread,
Mar 18, 2011, 12:13:34 PM3/18/11
to Prototype & script.aculo.us
Hi again,

Sorry for the double-post, somehow I managed to forget to say: My
mechanism isn't just (slightly) more long-winded, it's also more
complex to understand -- it demands more of the programmer. That's its
real downside, not the trivial difference in the length of

method.$super.call(this, arg);

vs.

$super(arg);

To use my mechanism, you have to understand how JavaScript functions
and the `this` keyword work, and it's very easy to make mistakes like
this:

method.$super(arg);

...which fails when the parent class's code tries to use `this`.

So it's about trade-offs. Prototype's mechanism is more newbie-
friendly, and there's real strength in that. My mechanism is
dramatically more efficient, and quite easy to use once you understand
the language a bit better, but not for newbies.

-- T.J. :-)

Luke

unread,
Mar 24, 2011, 12:54:53 PM3/24/11
to prototype-s...@googlegroups.com, T.J. Crowder
It's late, but I have to ask something though that it still don't understand. Why doesn't prototype just add a reference to the parent-class in subclasses? Like

klass.prototype.superclass = superclass

...in Class.Create. Is it because the this-reference would go out of scope?

T.J. Crowder

unread,
Mar 24, 2011, 1:34:03 PM3/24/11
to Prototype & script.aculo.us
Hi

On Mar 24, 4:54 pm, Luke <kickingje...@gmail.com> wrote:
> It's late, but I have to ask something though that it still don't
> understand. Why *doesn't* prototype just add a reference to the parent-class
> in subclasses? Like
>
> klass.prototype.superclass = superclass
>
> ...in Class.Create. Is it because the *this*-reference would go out of
> scope?

You mean so you could reference it in instances via `this.superclass`?
Because of the grandchild problem. When you look at implementing a
superclass/subclass hierarchy with JavaScript, it's easy to create one
that will work one level deep (Parent->Child). Making it work
GrandParent->Parent->Child and deeper is difficult.

Remember that `this` always refers to the actual object, so if your
Child code calls the parent's `foo` via
`this.superclass.foo.call(this)` (recall the `.call(this)` is needed
to preserve `this`), that's fine and it works great. But what happens
when the Parent's code then does `this.superclass.foo.call(this)`?
Right! Exactly the same thing, because it's using the same property
(`superclass`) of the same object (`this`), and so it calls *itself*.
Infinite loop time.

Here are a couple of examples. I haven't used Prototype, but I've done
what Prototype would effectively be doing if it worked as you were
suggesting above:

http://jsbin.com/esalu5 <-- Parent->Child, all fine and dandy
http://jsbin.com/esalu5/2 <-- GrandParent->Parent->Child, loop city

There is *no* way to use a property on `this` (inherited from the
prototype or otherwise) to refer to the superclass, not that works
more than one level deep. It can't, because if you start with `this`,
all levels are working with the same data. That's why I based my
mechanism on the actual function objects themselves; they're unique,
and they're right there to hand. :-)

joe t.

unread,
Mar 24, 2011, 10:48:46 PM3/24/11
to Prototype & script.aculo.us
i'd been kicking the sand wondering if i should go ahead and ask you
to re-post that link, because i'd seen it months ago and then lost it.

Glad this came up again (as i'm sure it will in the future) so i could
just jump at the link and say thank you.

And you're exactly right about the complexity trade-off. Had your
approach been implemented in Prototype to save on performance, i never
would have understood it when i first started learning Prototype. Now
that i know it really well, and understand the fundamentals of "this"
much better, i'm going to adopt your method.
-joe t.

Luke

unread,
Mar 25, 2011, 4:19:47 AM3/25/11
to prototype-s...@googlegroups.com, T.J. Crowder
Ok, Thanks a lot (again!) T.J.! :)
Reply all
Reply to author
Forward
0 new messages