super constructor calls for max/min classes

# Luke Hoban (13 years ago)

Two questions on maximally minimal classes.

  1. Should a super constructor call be made if no derived constructor is specified?

The wiki and spec drafts suggest that no super call is made automatically on behalf of the developer if they leave off a constructor declaration in a derived class. So the example below would run without error and print 'undefined'.

class Animal { constructor(name) { this.name = name; } }
class Snake extends Animal { }
var sam = new Snake("Sammy the Python")
console.log(sam.name)

This looks likely to cause problems in practice. Would it be better to behave similar to Ruby/CoffeeScript where the default constructor of a class with a super class specified is to apply the super constructor with the same arguments? So the above would print 'Sammy the Python'.

  1. Should explicit constructors without any calls to super be an error in classes with declared super classes?

Similar to the first issue above - the wiki and spec draft say that the following runs without error and prints 'undefined'.

class Animal { constructor() { this.data = "hello"; } }
class Snake extends Animal { constructor() { } }
var sam = new Snake()
console.log(sam.data)

Again, it looks easy to accidentally miss making a super constructor call from an explicit derived class constructor. There's less that can be usefully done about this case than in #1 above. But this might be a case where a stronger static check is warranted. Something like: report an early error when a constructor in a class with a super class specified does not include any super call. This may have to be left to lint tools though.

Luke

# Allen Wirfs-Brock (13 years ago)

On Jul 7, 2012, at 8:14 PM, Luke Hoban wrote:

Two questions on maximally minimal classes.

  1. Should a super constructor call be made if no derived constructor is specified?

The wiki and spec drafts suggest that no super call is made automatically on behalf of the developer if they leave off a constructor declaration in a derived class. So the example below would run without error and print 'undefined'.

class Animal { constructor(name) { this.name = name; } } class Snake extends Animal { } var sam = new Snake("Sammy the Python") console.log(sam.name)

This looks likely to cause problems in practice. Would it be better to behave similar to Ruby/CoffeeScript where the default constructor of a class with a super class specified is to apply the super constructor with the same arguments? So the above would print 'Sammy the Python'.

so would:

class Example extends Object {}; var x = new Example();

call the Object constructor (as a function) with no argument. That is going to allocate an extra object that gets immediately discarded but I guess that is harmless. However, it's not clear to me why class Example2 {} shouldn't also make that pointless call, if we do it in the first case.

Also not that Object.prototype.constructor is a writable property. That means that somebody could hijack such automatic calls to and take control of the initialization of all such objects.

I see where you are coming from on this, however to some degree the logic in support of it it feels awfully close to the logic for providing auto coercions (which we have, but most people regret).

Not everybody seems to agree that what Ruby does is desirable. See the comments on odetocode.com/Blogs/scott/archive/2010/07/13/ruby-initialize-and-super.aspx

  1. Should explicit constructors without any calls to super be an error in classes with declared super classes?

I feel much stronger that this should be a no. Maybe the whole point of the subclass is eliminate something undesirable in the super class constructor. This is a dynamic language, many things are possible. Let's not over constrain the developer.

Also, if it wasn't clear from above, I'm not a big fan of class Example2 {} having different semantics from class Example2 extends Object {}

(although if you read the ES6 draft carefully you will see that class Example2 {} is actually specified to be equivalent to class Example2 extends Object.prototype {} this ensures that the Example2 class object does not inherit Object class methods such as Object.defineProperty. If you want to inherit those methods you would implicitly say class Example2 extends Object {}; )

Similar to the first issue above - the wiki and spec draft say that the following runs without error and prints 'undefined'.

class Animal { constructor() { this.data = "hello"; } } class Snake extends Animal { constructor() { } } var sam = new Snake() console.log(sam.data)

Again, it looks easy to accidentally miss making a super constructor call from an explicit derived class constructor. There's less that can be usefully done about this case than in #1 above. But this might be a case where a stronger static check is warranted. Something like: report an early error when a constructor in a class with a super class specified does not include any super call. This may have to be left to lint tools though.

I think lint tools would be a fine place for this sort of audit rule.

# Russell Leggett (13 years ago)

On Mon, Jul 9, 2012 at 8:47 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:

On Jul 7, 2012, at 8:14 PM, Luke Hoban wrote:

Two questions on maximally minimal classes.

  1. Should a super constructor call be made if no derived constructor is specified?

The wiki and spec drafts suggest that no super call is made automatically on behalf of the developer if they leave off a constructor declaration in a derived class. So the example below would run without error and print 'undefined'.

class Animal { constructor(name) { this.name = name; } } class Snake extends Animal { } var sam = new Snake("Sammy the Python") console.log(sam.name)

This looks likely to cause problems in practice. Would it be better to behave similar to Ruby/CoffeeScript where the default constructor of a class with a super class specified is to apply the super constructor with the same arguments? So the above would print 'Sammy the Python'.

Reaching back to a previous version of "class as operator", this came up

as well. esdiscuss/2011-November/018599

Here, I believe even Allen was warming to the idea of a pass through constructor. Personally, I think it makes a lot of sense, because then it would effectively work like an overloaded method, which I like, although it would actually have to be a new constructor. It seems like a very reasonable option.

so would:

class Example extends Object {}; var x = new Example();

call the Object constructor (as a function) with no argument. That is going to allocate an extra object that gets immediately discarded but I guess that is harmless. However, it's not clear to me why class Example2 {} shouldn't also make that pointless call, if we do it in the first case.

I believe somewhere in that thread I mentioned, somebody suggested skipping a super call to Object's constructor. I think this would be reasonable.

Also not that Object.prototype.constructor is a writable property. That means that somebody could hijack such automatic calls to and take control of the initialization of all such objects.

I see where you are coming from on this, however to some degree the logic in support of it it feels awfully close to the logic for providing auto coercions (which we have, but most people regret).

Not everybody seems to agree that what Ruby does is desirable. See the comments on odetocode.com/Blogs/scott/archive/2010/07/13/ruby-initialize-and-super.aspx

  1. Should explicit constructors without any calls to super be an error in classes with declared super classes?

I feel much stronger that this should be a no. Maybe the whole point of the subclass is eliminate something undesirable in the super class constructor. This is a dynamic language, many things are possible. Let's not over constrain the developer.

Also, if it wasn't clear from above, I'm not a big fan of class Example2 {} having different semantics from class Example2 extends Object {}

(although if you read the ES6 draft carefully you will see that class Example2 {} is actually specified to be equivalent to class Example2 extends Object.prototype {} this ensures that the Example2 class object does not inherit Object class methods such as Object.defineProperty. If you want to inherit those methods you would implicitly say class Example2 extends Object {}; )

Similar to the first issue above - the wiki and spec draft say that the following runs without error and prints 'undefined'.

class Animal { constructor() { this.data = "hello"; } } class Snake extends Animal { constructor() { } } var sam = new Snake() console.log(sam.data)

Again, it looks easy to accidentally miss making a super constructor call from an explicit derived class constructor. There's less that can be usefully done about this case than in #1 above. But this might be a case where a stronger static check is warranted. Something like: report an early error when a constructor in a class with a super class specified does not include any super call. This may have to be left to lint tools though.

I think lint tools would be a fine place for this sort of audit rule.

Yes, I think lint is perfect for missing calls to super as opposed to missing constructors.

# Erik Arvidsson (13 years ago)
  1. I'm in favor in principal.

We just need to work out the details here. Is it really only Object that has issues? Aren't all the existing "classes" problematic since they can be called instead of constructed?

class MyError extends Error {}

Error.call(this) returns a new Error object.

  1. Leave it to the linters.
# Allen Wirfs-Brock (13 years ago)

On Jul 10, 2012, at 12:16 PM, Erik Arvidsson wrote:

  1. I'm in favor in principal.

We just need to work out the details here. Is it really only Object that has issues? Aren't all the existing "classes" problematic since they can be called instead of constructed?

I address some of these issues in strawman:subclassable-builtins (which I really need to finish up a few loose ends)