Not forcing super in derived class's constructor?

# Glen Huang (9 years ago)

TLDR: If this is used without super (which should be statically analyzable), let it refer to Object.create(new.target.prototype). Otherwise, let super creates what it refers to.

I know the reason to force super in derived class's constructor is to make sure this refers to the exotic object the base class might allocate.

But I bet in real world, extending base classes who only create ordinary objects is more common than extending those create exotic objects. And forget to call super is going to be frequent mistake. In es5, when you extend a class like this

function Foo() {
	Bar.call(this)
}
Foo.prototype = Object.create(Bar.prototype);

this refers to Object.create(new.target.prototype). So I wonder if we can keep this behavior, making things less surprising when people transit to es 2015?

# Glen Huang (9 years ago)

No one care to comment? Hope I didn't mistake how super works.

# Edwin Reynoso (9 years ago)

Give it time, people don't check this everyday, and if they do. Wait till they come up with an answer, also today's mother's day. Ik it's been 3 days but again people don't check this everyday. I for one don't know the answer to this.

# Glen Huang (9 years ago)

Not hastening to get an answer. Just want to make sure I didn't ask a question with an incorrect premise.

Thanks for the reminding, very assuring.

# Claude Pache (9 years ago)

Le 11 mai 2015 à 05:26, Glen Huang <curvedmark at gmail.com> a écrit :

No one care to comment? Hope I didn't mistake how super works.

On May 8, 2015, at 11:05 AM, Glen Huang <curvedmark at gmail.com> wrote:

TLDR: If this is used without super (which should be statically analyzable), let it refer to Object.create(new.target.prototype). Otherwise, let super creates what it refers to.

I know the reason to force super in derived class's constructor is to make sure this refers to the exotic object the base class might allocate.

But I bet in real world, extending base classes who only create ordinary objects is more common than extending those create exotic objects. And forget to call super is going to be frequent mistake. In es5, when you extend a class like this

function Foo() {
	Bar.call(this)
}
Foo.prototype = Object.create(Bar.prototype);

this refers to Object.create(new.target.prototype). So I wonder if we can keep this behavior, making things less surprising when people transit to es 2015?

Yes, in the course of refactoring your classes, you are indeed forced to transform your old clunky Bar.call(this) invocations into new shiny super() invocations, and it is IMHO a good thing. I don't see personally the point to support deprecated patterns in new syntactic forms.

But anyway, if refactoring your old classes is too difficult (for it might need more complicated changes that the aforementioned simple substitution), you are not obliged to: the old ES3/5 "classes" will continue to work, and will even be usable as superclass (in the extends clause) of new ES2015-classes.

# Glen Huang (9 years ago)

Thanks for the reply.

it is IMHO a good thing

I like the way you call the old way of doing things clunky and "super" shiny (no sarcasm intended). :)

However, forcing super has one major drawback: it becomes cumbersome when you don't want to call superclass's constructor(), that you want to define a new constructor() to override it.

When that happens, you have two choices:

  1. don't use "this", create a new object and return it.
  2. use the old way to create "class"

.#1 is against humanity. It's like a new law enforces that if your grandma is near by and you only want to talk your mom, you are forbidden to call her mom, otherwise you will be thrown off a bridge.

.#2 is deprecated. For example, it doesn't correctly set enumerability unless you do it explicitly.

I don't see personally the point to support deprecated patterns

I agree the function pattern should be deprecated, but not the concept of "you will be given a shiny new object when the constructor is called".

In the case of my proposal, it becomes "you will be given a shiny new object when the constructor is called unless you call super in it, which creates a shiny new object itself".

I think it a lot reasonable than "you must use super to get the new object, if you don't want to trigger its side effects, create the object yourself but you can't call it 'this' like you accustom to." unless I missed something and it's possible to use "this".

But I still feel automatically creating a new regular object if no super is called makes life a lot easier.

# Claude Pache (9 years ago)

Le 12 mai 2015 à 04:51, Glen Huang <curvedmark at gmail.com> a écrit :

However, forcing super has one major drawback: it becomes cumbersome when you don't want to call superclass's constructor(), that you want to define a new constructor() to override it.

When that happens, you have two choices:

  1. don't use "this", create a new object and return it.
  2. use the old way to create "class"

To be short, for me, there are two more choices:

  1. maybe subclassing that particular class is not the correct abstraction here;
  2. correct the design of the super-constructor, so that it is able to just do the minimum required stuff.

Also, as an alternative to your choice #2, there is the following possibility that I don't propose very seriously, although I wonder if it might be more semantically correct than subclassing in your use case. Instead of class B extends A { }, write:

class B { }
Object.setPrototypeOf(B, A)
Object.setPrototypeOf(B.prototype, A.prototype)
# Glen Huang (9 years ago)

maybe subclassing that particular class is not the correct abstraction here;

I think the need for inheriting methods and the need for a totally different way of preparing the object are orthogonal.

correct the design of the super-constructor, so that it is able to just do the minimum required stuff.

This sounds like the correct way of doing things, but the reality is usually that when someone writes the base class, it might not be obvious that it will be derived, so the base class isn't designed with some param combination will just do the minimum required stuff, and when people do want to derive it, providing a new constructor(), they are unable to ask super to just provided an untouched new object.

there is the following possibility that I don't propose very seriously

Didn't think of this method before, thanks for the heads up. But it's imperative and rely on the fact that "extends" currently only sets the prototypes of two object. I'm afraid it won't be enough once "extends" does something more in a later spec.

I wonder what's so terrible about creating a new object when there is no super that you just want to avoid it?

# Brendan Eich (9 years ago)

Glen Huang wrote:

maybe subclassing that particular class is not the correct abstraction here;

I think the need for inheriting methods and the need for a totally different way of preparing the object are orthogonal.

There's a saying taught (I'm told) in law school: "hard cases make bad law."

It means systems of laws (real world or JavaScript language laws) must aim for the common cases and avoid both over-prescribing or over-reaching, and trying to cover all use-cases with all affordances. You can't "have it all", in short.

Your case seems hard, at a glance. If you can show it's common, please cite evidence.

I wonder what's so terrible about creating a new object when there is no super that you just want to avoid it?

Your proposal was:

"TLDR: If this is used without super (which should be statically analyzable), let it refer to Object.create(new.target.prototype). Otherwise, let super creates what it refers to."

I think you meant "If this is used without super in a subclass constructor (a subclass has an extends clause; this condition should be statically analyzable), let it refer to Object.create(new.target.prototype). Otherwise, let super creates what it refers to.

We discussed this in the long course of coming to ES6 classes, if I recall correctly. What you propose makes a footgun. It's too easy to forget to call super and get an object that isn't correctly initialized. What you want to do, bypass the super-constructor but wire up the proto-chain, can be done the hard way, appropriate for a hard case (which makes bad law).

# Glen Huang (9 years ago)

Thanks for the motto. Another TIL. :)

My proposal is based on the assumption that people didn't complain much about it's easy to forget calling super in es5 code.

But I think you are right, preventing footgun is more important than catering to a hard case.