super in constructor should be distinct (Re: (Weak){Set|Map} subclassing)

Herby Vojčík herby at mailbox.sk
Wed Dec 5 05:45:56 PST 2012


Allen Wirfs-Brock wrote:
> super(...) is just shorthand for super.constructor(...) (when it
> occurs in a constructor) so it is just a use of [[Call]]. No magic.

[[Call]]/[[Init]] aside.

After reading the spec, it is really so that super(...) in constructor 
is in fact super.constructor(...), because
  - constructor has MethodName "constructor"
  - super(...) has generic super[MethodName](...) semantics.
IOW, constructor is compiled as any other method with respect to 
semantics of its code (it gains its [[Construct]] and .prototype magic 
later, but these are external and do not touch the code).

It is bad for two reasons:

1. It will fail. There is lot of examples of code like:

   function Foo() { ... }
   Foo.prototype = Object.create(Bar.prototype);
   // or even (!!!) Foo.prototype = new Bar();
   Foo.prototype.baz = function () {...};
   ...

all over the web, in libraries etc.
Some people add "Foo.prototype.constructor = Foo;" there, but some 
don't. Now you see what happens if I try to use the new class:

   class Quux extends Foo {
     constructor() { ....; super(...); ... }
   }

super(...) call fails, because there is no Foo.prototype.constructor.

What's additional risk, the .constructor can be writable/configurable, 
so something may be injected / it may be deleted. It is not what 
developers assume. This relates to the:

2. It is not right (imho). Constructors are not methods (is there a 
movement of making them into ones? I doubt, constructor methods are too 
in-grained in the ECMAScript structure).

Constructors are not methods, they are much more external to the class. 
They _are_ the class, so what nearly every developer assumes when 
calling super(...) in the constructor with extends Foo to call Foo. Not 
Foo.prototype.constructor, which is way too brittle.

It is the common practice now, Foo.apply(this, arguments) or 
Foo.call(this, ...). The ES6 super(...) does something else.


Therefore, I propose super(...) has different semantics for constructor 
methods*. It should do roughly (ctr being the constructor function):

1. Get [[Prototype]] of ctr into proto, fail if problem.
2. If proto is constructor function,
   2.1. [[Call]]** proto with thisArg of this, and argument list equal 
to super(...) argument list
   2.2. return the completion of 2.1.
3. Otherwise, throw.***

> Allen

Herby

* That is, I'd like to see it in freeform constructor methods as well. 
Only with super(...) shortened form. It's semantics is straightforward 
and prompts replacing Bar.call(this, ...) with saner super(...); of 
course, one must rewire Foo.__proto__ = Bar; to get this. May help the 
transition and understanding that class has constructor inheritance as 
well, not only prototype inheritance.
** Or [[Init]] if adopted.
*** Alternatively, revert to super.constructor(...). I don't like it, 
though.


More information about the es-discuss mailing list