Proposal to fix super and new inconsistency, future-proofing broader uses of new operator

Herby Vojčík herby at mailbox.sk
Sat Aug 31 03:51:00 PDT 2013


Hello!

PROBLEM
====

In the present state of the spec, there is little inconsistency between 
behaviour of new and super.

What these operation roughly do is:

   new Foo(...args) is
     Foo.call(Foo[@@create](), ...args)
   super(...args) inside constructor is
     __superclassproto__.constructor.call(this, ...args)

This is elegant and consisten solution - super behaves as in any other 
method - calling superclass's version of itself.

Since Foo.prototype.constructor is set to Foo by default, no 
inconsistency is observed in default case - super(...args) calls the 
same function from subclass of Foo and in new Foo.

But if constructor is changed (or deleted / not defined), inconsistency 
appears - new still calls Foo, but super calls different function (or 
fails if there is no .constuctor in proto chain).

My gut feeling is that new Class and super in SubClass should do the 
same thing. Also, if this IMO bug begins to be exploited, to "have 
different initialization of own instance versus subclass one", there is 
no way back.

SOLUTION
====

There is elegant solution for this by redefining new to do roughly:

   new Foo(...args) is
     Foo[@@create]().constructor(...args)

Compared to previous semantics, this is much cleaner and understandable, 
and in par with super philosophy of "treat 'constructor' as just another 
method".

For default cases, this works identically with the formed definition.

For `class` keyword, if you change constructor method of an existing 
class, this semantics nicely implements your intent - to change the way 
howe class Foo is initialized (in both new and super).

Remaning scenario is changed .constructor of constructor function.
Here, it can be changed directly (you change .constructor of existing 
.prototype) or indirectly (you change .prototype of the constructor 
function). Both would break existing web.

The silent assumption of this proposal is, that the former case 
(changing .constructor of default .prototype) is rare if it ever 
appears, though I did not search for this.

The second case is much more common: one redefines .prototype of a 
function, but does not define .constructor there (there was no real 
need). I would propose guard against this case - whenever the .prototype 
of a function is changed, the new would use old, legacy semantics. 
Constructor functions with non-changed .prototypes, as well as `class`es 
(which have .prototype non-writable) would work fine with the new, 
cleaner semantics.

FUTURE PROOFED BROADER NEW
====

This change decoupled the need of the Foo in `new Foo` being callable - 
so the new semantics of new allows any object having @@create defined to 
be usable inside new - the initialization of the instance is nothing 
more than just calling 'constructor' method with appropriate args, so 
the new instance is responsible for initializing itself, no matter who 
was its creator/allocator.

ISSUES
====

1. It is not known if the case of changing .prototype.constructor 
without changing .prototype itself on legacy constructor functions is 
really rare or it has its legitimate use and is spread.
2. Instance does not know its creator/class (you cannot do generic 'new 
this.constructor(...args)').

Herby


More information about the es-discuss mailing list