Promise() vs. new Promise()

Allen Wirfs-Brock allen at wirfs-brock.com
Wed Aug 20 17:19:15 PDT 2014


On Aug 20, 2014, at 1:11 PM, Brendan Eich wrote:

> Alex Kocharin wrote:
>> I mean, not using `new`. JS is a functional language after all, and having a function that can't be called as a function is weird.
> 
> Gotcha, and my point was not to mandate `new`, just allow it. I thought we relaxed things so `new` was optional if there was no issue leaving it out:
> 
> http://esdiscuss.org/topic/make-class-constructors-work-with-call-too
> 
> But that was a while ago.
> 
>> If constructor supports calling it without new, I can pass it around as a function, do this for example:
>> 
>> ```
>>> >  ['foo', 'bar'].map(Error)
>> [ [Error: foo], [Error: bar] ]
>> ```
>> 
>> With mandatory `new` this would be much less elegant.
> 
> Agreed, mandating `new` where there's no issue (legacy, e.g., Date; or other?) would be Bad(tm).
> 
>> I was about to construct realistically-looking chain with [fn1, fn2, fn3].map(Promise).forEach(addThenHandler), but FF already seem to throw on it.:(
> 
> Hrm.

We've talked about this several times at TC39 meetings and the consensus has always been to not conflate the meaning of new C() and C() for new built-in constructors.

The problem is that is difficult to correctly code  (in ES code) a constructor function that has this behavior. Even assuming there is a way to reliably determined if you were called  with or without `new` you still need to have to separate logic paths for the two cases and there are other subclassing issues (more below).  For these reasons, we should expect that most constructors defined using `class` will require use of  `new` to instantiate them. 

If as  matter of principle, we shouldn't be specifying built-ins that do things in a way that is difficult to reproduce in ES code and we shouldn't be encouraging  usage patterns  that confuse people or are error prone to implement.

If you want a purely functional way to do instantiation, then I suggest adding a `new` method to Function.prototype:
`
   Function.prototype = function new(...args) {return new this(...args);
`

Back to subclassing, assume that Map was implemented in ES, something like this:
( **called-via-new** is a placeholder for new syntax)
`
class Map {
   constructor(...args) {
      if (! **called-via-new**) return new Map(...args);
      // initialize this using args
   }
   //methods
}

//then the  following do the same thing:
var m = Map();
var m = new Map;
`
You might then,  reasonably expect to code
`
class ExtendedMap extends Map {
   additionalMethod() {}
}
`
but if you do, you will discover:
`
var m = ExtendedMap();  //actually creates an instance of Map
var m = new ExtendedMap; //actually creates an instance of ExtendedMap;
`
the problem is that the Map constructor explicitly references `Map` rather than the actually invoked constructor and there is no good may to determine what the actual constructor was.  So to make the subclass work correctly you have to remember to code the subclass as:
`
class ExtendedMap extends Map {
   constructor(...args)  {
        if (! **called-via-new**) return new ExtendedMap(...args);
        return new super(...args);
   }
   additionalMethod() {}
}
`

and repeat a pattern like this in every other subclass you write.  Much better to just avoid this sort ot two-faced constructors.

Alen


More information about the es-discuss mailing list