The proposed Class Instance Fields leaves one question open: How do you create constant fields with access to constructor arguments?
This is an idea for a syntactical feature that can address this.
The syntax builds upon the current class instance fields proposal and existing class methods.
It adds an argument list to the class itself which provides a new scope around the class. The scope semantics of the class body itself doesn't change. I.e. methods and fields are not accessible without the this.
prefix.
class Point2D (inX, inY) {
x = inX;
y = inY;
}
Desugars to:
class Point2D {
constructor(inX, inY) {
this.x = inX;
this.y = inY;
}
}
When the constructor arguments are referred to within methods or field initializers, the method itself is not bound. It is still on the prototype. The method refers to a private slot on whatever this
happens to be when the method is invoked.
class Point2D (x, y) {
measure() {
return Math.sqrt(x * x + y * y);
}
}
Pseudo-desugaring to private fields:
class Point2D {
#_desugared_x;
#_desugared_y;
constructor(x, y) {
this.#_desugared_x = x;
this.#_desugared_y = y;
}
measure() {
return Math.sqrt(
this.#_desugared_x * this.#_desugared_x +
this.#_desugared_y * this.#_desugared_y
);
}
}
At least conflicting constructor arguments is a syntax error. Having both forms in the same class might also need to be a syntax error.
class Point2D (x, y) {
constructor(x, y, z) { // syntax error?
}
}
Static methods can be interleaved within this scope but they don't have access to the constructor argument.
class Point2D (x, y) {
static compare(a, b) {
return x; // syntax error?
}
}
When the class extends another one, the parenthesis have to go before the extends since otherwise it would be ambiguos with a function call.
class Point3D (x, y, z) extends Point2D {
#z = z;
}
Desugars to:
class Point3D extends Point2D {
#z;
constructor(...args) {
super(...args);
this.#z = args[2];
}
}
This might be proposed to ECMAScript in the future but is not yet a concrete proposal. It at least shows a plausible route forward for solving this problem after field initializers.