Referencing `super`
class ClientForm extends Form{
submit() {
super.submit(); // Invokes Form.submit
let superSubmit = super.submit; // Reference to Form.submit
superSubmit(); // Invokes, but `this` is now undefined; not sure if intended
just like:
this.submit(); // Invokes ClientForm.submit
let thisSubmit = this.submit; //Reference to ClientForm.submit
thisSubmit(); //invokes, but 'this' in now undefined
This is how properties and method invocations work in JS. All that the use of 'super' does is change the place the property lookup starts. Otherwise super
is equivalent to this
in the above code.
super(); // Invokes Form.submit
semantically equivalent to super.submit();
just a short cut
let superSubmit2 = super; // Error: "Unexpected token ;"
in some languages, such a unqualified 'super' reference would be equivalent to 'this'.
We intentionally made it an error for that reason. I perhaps could be convinced that it should mean the same as super.submit
.
But in that case,
superSubmit2()
would still not be the same thing as
super();
or
super.submit();
Thanks for your response, Allen. I'm not sure what convincing I can do. To
me it seems odd that super()
is the same as super.submit()
but super
is not the same as super.submit
, but perhaps to others that seems
perfectly fine. An alternative to this suggestion would be super()
should
not be the same as super.submit()
; and instead super()
is either
illegal or calls constructor
.
I sympathize; I have always found the fact that bare super()
works to be confusing.
When a bare super() call appears in a method (whether constructor or not)
it can only have one meaning and that's a call to a method of the same
name in the parent class. This isn't particularly innovative: John Resig's
Simple JavaScript Inheritance—arguably one of the most widely used (many
clones, forks and spin-offs exist) "abstract class" techniques—provides
this._super()
which does the same thing that ES6 super() does. This
pattern existed before and has been repeated throughout many libraries that
have stood out over the years: Prototype, Dojo, Ext.js and certainly
others. CoffeeScript implements super() this way as well.
On Wed, Aug 6, 2014 at 12:38 AM, Rick Waldron <waldron.rick at gmail.com> wrote:
This isn't particularly innovative:
Note: I don't mean to imply that any of the examples I gave here are innovative either, they all copy the design (in a quasi-super-via-API way) and semantics that exist in other languages and I'd bet specifically Ruby. (Python requires the fully qualified name, which should be proof that JS has it right by allowing both forms)
My preference would be for super()
to remain as-is, and give super
the
equivalent semantics: reference a method of the same name in the parent
class (as opposed to invoke). I only provided the alternative since my
ultimate view is that super
and super()
should function similarly
(either both be legal, or both be illegal).
use of unqualified super in this manner has a long history in OO programming language. It’s too late this evening for me to dig up references for you but it dates to at least language designs of the early 1980. Smalltalk didn’t do it, but mainly because it does doesn’t use a function invocation notation for method method calls.
The the early Smalltalk develpment experience clearly showed that methods almost always do super invokes of the same method name as the caller. Unqualified super is a shorthand for this most common. It is so common, that I always react to seeing a qualified super invocation as a very exceptional case that I should look very closely at to understand why a something other than the current method is being super invoked.
The need to qualify super with a different method name is so rare, that some languages support nothing but unqualified super. But there are a few situations were it is quite useful and it is worth having in an OO programming language. It’s kind of ironic that I recall previous discussions during the development of the ES6 class design where I had to justify why we should allow super to be qualified with a property name.
On Aug 5, 2014, at 10:11 PM, Brett Andrews <brett.j.andrews at gmail.com> wrote:
My preference would be for
super()
to remain as-is, and givesuper
the equivalent semantics: reference a method of the same name in the parent class (as opposed to invoke). I only provided the alternative since my ultimate view is thatsuper
andsuper()
should function similarly (either both be legal, or both be illegal).
I addressed this in my first replay
On Aug 5, 2014, at 6:37 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
in some languages, such a unqualified 'super' reference would be equivalent to 'this'.
We intentionally made it an error for that reason. I perhaps could be convinced that it should mean the same as 'super.submit’.
But it isn’t something we necessarily need to consider for ES6. We can always relate the error if we find that people really want to do that. I suspect we won’t.
expressions like:
super = expr;
are probably good examples of DDWIDM. (Don’t Do What I Didn’t Mean)
in a method named ‘foo’
super = expr;
would be equivalent to
this.foo = expr;
if ‘foo' was not already defined as an access property (and methods usually are data properties). Quite likely something the programmer didn’t mean.
Rick Waldron wrote:
When a bare super() call appears in a method (whether constructor or not) it can only have one meaning and that's a call to a method of the same name in the parent class. This isn't particularly innovative: John Resig's Simple JavaScript Inheritance[0]—arguably one of the most widely used (many clones, forks and spin-offs exist) "abstract class" techniques—provides
this._super()
which does the same thing that ES6 super() does. This pattern existed before and has been repeated throughout many libraries that have stood out over the years: Prototype, Dojo, Ext.js and certainly others. CoffeeScript implements super() this way as well.
CoffeeScript imitated Ruby here.
Smalltalk had super rather than self sends, but you had to send with the
full selector (think method name). Java has super() in constructors but
requires super.method() in methods. I'm cool with super() in methods, I
forgot we disallowed naked super
, and my gut says we would support it
as equivalent to this
.
On 06.08.2014 06:38, Rick Waldron wrote:
When a bare super() call appears in a method (whether constructor or not) it can only have one meaning and that's a call to a method of the same name in the parent class. This isn't particularly innovative: John Resig's Simple JavaScript Inheritance—arguably one of the most widely used (many clones, forks and spin-offs exist) "abstract class" techniques—provides
this._super()
which does the same thing that ES6 super() does. This pattern existed before and has been repeated throughout many libraries that have stood out over the years: Prototype, Dojo, Ext.js and certainly others. CoffeeScript implements super() this way as well.
I wonder why you chose that way in the spec. An issue I can see here is that mixin-based inheritance on a per-method basis won't work properly as expected.
In my game engine lycheeJS, I'm using a mixin based inheritance to allow
- inclusions based on feature detection (html, nodejs, v8gl, etc.)
- inclusions in a specified order (a la a extends b extends c)
- overwrites of your method with super calls to both via direct b.prototype (or c.prototype) access
- mixin based includes that allow inclusions of single methods of other classes
Simplified Example:
var a = function(settings) {
// moar stuff
b.call(this,settings);
c.call(this,settings);
};
a.prototype = { render: function() {
c.prototype.render.call(this);
doCustomStuffInBetween();
b.prototype.render.call(this);
}};
The last reasons (c, d) are very important to my game engine, because it allows inclusion of single "public" methods. For example, you can reuse a 2D render() method of a ui.State for rendering the UI layer, then draw something in 3D on top of it via the game.State.
To access those namespaces properly, I have to create all definitions inside a closure with "dynamic" arguments. For example:
lychee.define('lychee.Renderer').exports(function(lychee, global, attachments) {});
lychee.define('game.Renderer').exports(function(lychee, game, global, attachments) {});
That allows me some essential things for the engine concept:
- every definition can be exported in a sandbox
- theoretically sandboxes of supports() (feature detection callbacks) are possible with new Proxies, currently they have to be executed global (different story of detecting type-based access to properties that are not existing at runtime and need to be dispatched)
- every definition can be serialized as a string and deserialized later, in sandboxed state without requiring access to global properties
- global accesses to properties or bound variables from outer scopes are easily traceable via try/catch
- global can be faked, it is not existing and not "window", it's a reference to lychee.Environment's global property.
An example of this mixin-based inheritance usage can be found here:
LazerUnicorns/lycheeJS/blob/development-0.8/projects/boilerplate/source/entity/Circle.js#L99
PS: If you are curious, install lycheeJS via README.md and run the localhost:8080/projects/boilerplate. Then call lychee.environment.serialize(); which creates the JSON that is deserializable (and injectable) at runtime.
Also, what is REALLY important (I can't stress that enough). JavaScript has a huge bonus when being compared to other programming languages. Namespaces are just objects in global scope that can exist at runtime, not necessarily at compile time or definition time. Access to lychee.game.Main is just new lychee.Environment().global.lychee.game.Main. The advantage here is that global can be faked as a sandbox and can be isolated to trace errors occuring on a per-definition base at runtime. (Namely remote debugging and remote code injection via websockets)
This leads to the possibility of inclusion of different libraries under a different namespace. All libraries think they have say the "game" namespace, but in fact, you can include them in a different project under a custom namespace. Every library, if sandboxed, will never pollute the "global namespace".
These features were the killer decision for me to use JavaScript as a game engine base, otherwise I probably would've used Lua (I'm honest).
I would recommend having those listed features in mind when talking about super() and namespaces, because the current spec still will prevent me from using native "class" keyword in JavaScript.
If newbies ask me if they should use classes, I say no to them, because I still think the featureset of a mixin-based inheritance (leading to fancy prototype copying and sealing) is still better.
I would recommend having those listed features in mind when talking about super() and namespaces, because the current spec still will prevent me from using native "class" keyword in JavaScript.
If newbies ask me if they should use classes, I say no to them, because I still think the featureset of a mixin-based inheritance (leading to fancy prototype copying and sealing) is still better.
That would be terrible advice, because classes in Javascript are just sugar for setting up a constructor and its associated prototype. Once you define a class, you can do any kind of dynamic tomfoolery that you like, including setting up mixins in the fashion you describe.
On Wednesday, August 6, 2014, Brendan Eich <brendan at mozilla.org> wrote:
CoffeeScript imitated Ruby here.
Yep, and probably the other quasi-super APIs I mentioned as well—that was in my follow up message.
On Wed, Aug 6, 2014 at 2:06 AM, Brendan Eich <brendan at mozilla.org> wrote:
Smalltalk had super rather than self sends, but you had to send with the full selector (think method name). Java has super() in constructors but requires super.method() in methods. I'm cool with super() in methods, I forgot we disallowed naked
super
, and my gut says we would support it as equivalent tothis
.
To clarify, you don't mean super === this
, right?
Rick Waldron wrote:
To clarify, you don't mean
super === this
, right?
The alternative is for bare super
to denote the same-named superclass
method bound to this
. That enables the equivalence Allen wrote based
on Brett's error citation:
let superSubmit2 = super; // Error: "Unexpected token ;"
superSubmit2(); // if no Error, this is equivalent to super()
But that breaks the other equivalence:
super.method(); ==== do { let s = super; s.method(); }
So you can see why bare super
is currently illegal! (Want a better
error message than the one Brett showed.)
If we make bare super
an error for now, in hopes of resolving this
conflict of equivalences later, which way do we think we'll resolve? We
ought to have an opinion now.
Yes, this is/was all clear (and I stand on the side of keeping bare super illegal), I was just curious about what your gut was saying ;)
Agree that a more explicit early error would be ideal.
On 06 Aug 2014, at 21:03 , Brendan Eich <brendan at mozilla.org> wrote:
If we make bare
super
an error for now, in hopes of resolving this conflict of equivalences later, which way do we think we'll resolve? We ought to have an opinion now.
Given how much super
does under the hood (if you call a method, you get a super/this hybrid), a stand-alone super
may give people the wrong idea (and I don’t see how it would be useful). It may be better to provide access to [[HomeObject]] (via Reflect?).
Rick Waldron wrote:
I was just curious about what your gut was saying ;)
I was going for alias to this
. But that argues against super()
as
short for super.method()
in method context. And that means Java, so
bleah :-P.
I don't think we need to do any special binding to this
for bare super
.
Perhaps it will help if I provide the original use case that led me to
this. I'm using AngularJS and declaring controllers as classes. I have a
base FormCtrl that extends (for example) a ClientFormCtrl. The cut-down
version of the constructor is this:
constructor($injector) {
$injector.invoke(super, this, {
formName: 'activityUpsertForm'
});
}
In this case, I don't care what super
is bound to, since under the covers
Angular is going to do super.apply(this).
I think the most obvious and least confusing/breaking way for super
to
function is for it to be the equivalent of let super = super.[method_name]
Brett Andrews wrote:
I think the most obvious and least confusing/breaking way for
super
to function is for it to be the equivalent oflet super = super.[method_name]
(No .[
there, that may be used in the future to mean something else
Some differences/oddities I noticed between referencing and invoking
super
. I briefly discussed this with Erik Arvidsson at google/traceur-compiler#1220. In essence, if you can invoke in two seperate ways, it seems logical that you should be able to reference in those two ways (super() ~= super.submit(); super != super.submit).class ClientForm extends Form{ submit() { super.submit(); // Invokes Form.submit let superSubmit = super.submit; // Reference to Form.submit superSubmit(); // Invokes, but `this` is now undefined; not sure if intended super(); // Invokes Form.submit let superSubmit2 = super; // Error: "Unexpected token ;" }