- Minimize changes to the existing ES6 class design.
- Allow for subclassing built-in classes which are not created by the typical two-phase initialization process.
- The choice between two-phase and single-phase initialization should be explicitly determined by syntax.
- Allocation and initialization can't be separated for many built-in classes and host-provided classes.
- Subclassing built-ins is the exception rather than the rule.
This design eliminates the special @@create
method.
The current class syntax is extended to allow an initializer within the header portion of the constructor. The initializer
is an arbitrary expression that, when evaluated, will provide the this
value for the constructor when invoked via
new
. When subclassing a built-in, the user will be responsible for using the initializer to provide a fully-constructed
instance of the built-in class. This requirement is applicable to all class descendants of built-in classes.
After the initializer is evaluated, the prototype of the resulting object is set to the class prototype.
All other elements of the design remain the same.
ClassConstructor:
constructor ( StrictFormalParameters ) ClassCreateExpression? { FunctionBody }
ClassCreateExpression:
extends LeftHandSideExpression
A constructor C
created with class syntax has roughly the following evaluation semantics:
- Evaluate function parameters (see
FunctionDeclarationInstantiation
) - If the constructor was invoked with [[Construct]]:
- Assert: The
thisValue
of the current function environment record is undefined. - If ClassCreateExpression exists:
- Let
obj
be the result of evaluating ClassCreateExpression using the parameter default evaluation scope. - ReturnIfAbrubt(obj)
- If Type(
obj
) is not Object, then throw a TypeError exception.
- Let
- Else
- Let
obj
be a newly created object. - Set the [[Extensible]] internal slot of
obj
to true.
- Let
- Set the [[Prototype]] internal slot of
obj
toC.prototype
. - Set the
thisValue
of the current function environment record toobj
.
- Assert: The
- Evaluate FunctionBody
Note that neither this
nor super
are allowed within the class initializer expression.
In practice this logic might have to be called from FunctionDeclarationInstantiation
and would require
internal refactoring.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColoredPoint extends Point {
constructor(x, y, color) {
this.color = color;
super(x, y);
}
}
class ColoredPoint extends Point {
constructor(x, y, color) extends new Point(x, y) {
this.color = color;
}
}
class Array1 extends Array {
constructor(x) extends [] {
this.push(x);
}
}
class Array2 extends Array1 {
constructor(x, y) extends new Array1(x) {
this.push(y);
}
}
This design allows two different techniques for initializing subclasses with the parent constructor. The choice of which to use might not be obvious to the user in some situations. Users will have to develop best practice standards to guide this decision.
- Would another token work better than
extends
for introducing the intializer expression? - It seems that inside of the initializer expression it would be useful to have some reference to the constructor
upon which
new
was called. Such a feature might be used to call a (possibly overridden) "static" class method. If so, how should that reference be exposed?