Good-bye constructor functions?

Allen Wirfs-Brock allen at wirfs-brock.com
Mon Jan 7 15:14:17 PST 2013


On Dec 30, 2012, at 11:47 AM, Herby Vojčík wrote:

> Hello,
> 
> thinking about class / new / super semantics and its problems, I came with an idea. Big change on the first look, but in fact, imho, not so big and making cleaner model.
> 
> 
> 
> tl;dr
> ----
> 
> Constructor function were workaround to specify constructor for a class without `class`. Now, having `class` nothing presribes the class being equivalent to its constructor. It always brought confusion and now with `constructor` empowered, it brings two-space problem. The `class` keyword can return plain objects, which have `[[Construct]]` calling the constructor of the class.
> 
> 
> 
> The rest is in https://gist.github.com/4413009. If you could please read it, it tries to explain and go a little into detail. I feel this is needed to do now, putting constraints away later would be harder, as I write below:
> 
> 
> 
> Conclusion
> ---
> 
> If `class` would decouple the value it produces (the class) from the constructor function, it would create cleaner and more future-friendly model of classes. Coupling class and its constructor function was a necessity in the past, but now they need not to be coupled. Class.prototype.constructor takes all the responsibility for initializing the instance. This change involves little risk (only pieces of code who actually [[Call]] the "class" for manually initializing it; super-constructor call can be replaced by `super` or `Superclass.prototype.constructor.{call,apply}`). The big spec pieces that involve creation of classes and their instances are already very near and the important pieces need just a few changes. The devil is in the details, the spec is still very 'constructor must be a function'-oriented, but this is mainly in textual parts, algorithms are already generic enough. One important reason to ponder `class` not being constructor function is not conserving this tight coupling which in world of ES6+ with `class` can be seen as antipattern; freeing the contraints now is much easier than doing it later, when this unnecessary coupling is repeated in `class` semantic.
> 
> 
> Thanks, Herby

OK, I ready you proposal.  I'll summaries it:

class Sub extends Super {
   constructor (...args) {
       super(...args);  //or super.constructor(...args)  because they mean the same thing.
  }
}

creates an (almost) ordinary object that is the initial value of the Sub binding.  The [[Prototype]] of Sub is the value of Super. Sub is created with a "prototype" property whose value is a new ordinary object whose [[Prototype]] values is Super.prototype.  Sub.prototype has a method property named "constructor" whose value is a super bound method function with the user specified body.  There is nothing special about this method, it it just like any other method defined in a class declaration.  In particular it does not have a [[Construct]] internal property.

The only thing exotic about Sub is that the object has a [[Construct]] internal method.  It does not have a [[Call]] internal method.  The definition of its [[Construct]] in almost JS pseudo code is:

      //internal method  this.[[Constructor]](argList):
      let newObj = this.@@create();  //eg Sub.@@create();
      let replacementObj = this.prototype.constructor.apply(newObj, argList);
      if (typeof replacementObj == "object" && replacementObj !== null) return replacementObj
      return newObj;

Implications:
   Sub === Sub.prototype.constructor evaluates to false.     
   To create a new instance of Sub say:
           new Sub()
   The following is a TypeError, because Sub does not have a [[Call]] internal method:
          Sub()
   The following is a TypeError, because Sub.prototype.constructor does not have a [[Construct]] internal method:
           new Sub.prototype.constructor()
    Sub.prototype.constructor can be called directly or as a method invocation including via a super call a subclass of Sub.
            
It seems to me, all you have accomplished here is to make it illegal to call a class object directly as a function.  If we were starting over that might be a reasonable design choice.  But we have a legacy to consider.  Even if we think we should discourage direct calls to class objects (I think I'm now in that camp) actually making it an error seems like it may be too big of a step away from the legacy conventions.

Allen






More information about the es-discuss mailing list