On Dec 24, 2013, at 6:41 PM, Sebastian Markbåge wrote:

> This is an esoteric and ugly use case but I'm not trolling. The default 
> constructor for a class which extends another is:
> 
> constructor(...args){ super(...args); }
> 
> Is there any reason it shouldn't return the value from super?
> 
> constructor(...args){ return super(...args); }
> 
> Basic constructors still have the quirky behavior of ES functions that they 
> can return any object and don't have to return the instantiated object. This 
> can be useful if they're used as functions or should return a placeholder 
> object, or other instance, for compatibility/legacy reasons. E.g. when you 
> have a custom instantiation process.
> 
> class Foo { constructor() { return {}; } }

This sort of allocation in the constructor body of a class definition will 
probably come to be viewed as an anti-pattern.   Note that:
       new Foo instanceof Foo
would evaluate to false given the above class definition.  Also, the 
Foo.prototype would not be on the prototype change of the returned value. 

There is a new factoring of responsibility and associated usage patterns that 
come along with ES6 class definitions.  We should no longer think in terms of a 
constructor function having the responsibility for both providing a new 
instance and initializing its state.  It is the responsibility of the @@create 
method to provide the object that is returned by the new operator.  The primary 
responsibility of the constructor body is to initialize that object. 

A better definition of the above Foo would be:

      class Foo {static [Symbol.create]() {return {}}}

However definition still produces instances that fail the instanceof test, so 
perhaps what you really want would be:

      class Foo {static [Symbol.create]() {return {__proto__: this.prototype}}}

It is difficult to design of a constructor body that behaves correctly in all 
five of these situations:  invoked via the new operator; invoked with a super 
call from a subclass constructor;  called directly; called via call/apply 
function with arbitrary things passed as the this value; and, called as a 
method. The ES6 spec. has to handle all for of those use cases for the legacy 
built-in constructors.  But I don't think we want to encourage people to do so 
for new abstractions defined using ES6 class definitions because in most cases 
what they produce will be buggy,

The usage patterns we should be teaching for class abstractions in ES6 are:

1) Always use the 'new' operator to create instances of a class.
2) Never call a class directly by name without 'new.
3) Class constructor bodies should initialize the instance object that are 
passed to them.
4) Class constructor bodies should only be called by the 'new' operator or as a 
'super' call from a subclass constructor body.
5) [experts] Define a @@create method if you need your class to have special 
allocation behavior. 

Also, don't model your classes after Array, Date, RegExp, Error, etc. as they 
violate these rules. Unlike those built-ins,  don't design your classes such 
that 'new' is optional. It is hard to get that right while still permitting 
subclassing. 

Regarding adding 'return' to the default constructor body.  It appears that 
technically it would be a benign change.  However, the only reason to do so 
would be accommodate superclasses that deviate from the above patterns.  In 
that case, you are probably already in the weeds.  I'm not sure that we should 
be trying to facilitate such deviations. 

> Currently, this behavior doesn't carry over to subclasses by default:
> 
> class Bar extends Foo {       }
> 
> You'd have to explicitly define a constructor that returns the value from the 
> super call.
> 
> Additionally, since DefineMethod is going to be exposed, does it make sense 
> to expose ReferencesSuper too?
> 
> Otherwise there is no way to detect, at runtime, if a default constructor 
> function is a top-level constructor (without a super), or a subclass with a 
> super. One of them can use toMethod but not the other.
> 

Can you describe the use case you are thinking about here?  The decision to do 
a super call in a constructor is normally a design time decision made by a 
programmer. 

allen

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to