In Javascript, class variables and class methods (defined using the static
keyword) are useful. When using them, I don't want to hard code the names of the classes, in case I refactor and change the name of a class. That's why class methods can get to class variables using this
, and instance methods can get to class variables and class methods using this.constructor
, as described in the MDN web docs.
However, it doesn't work as I'd expect when using subclasses. Each subclass gets a separate uidCounter
. I want the uid to be unique across all instances of the superclass, not the subclass.
class Test {
static uidCounter = 1000;
static uid() {
this.uidCounter += 1;
return this.uidCounter;
}
constructor(tag) {
this.uid = this.constructor.uid();
}
toString() {
return `${this.constructor.name} uid=${this.uid}`;
}
}
class A extends Test {
}
class B extends Test {
}
console.clear();
console.log(new A().toString());
console.log(new A().toString());
console.log(new B().toString());
console.log(new B().toString());
It works as I intended if I hard code the name of the class:
class Test {
static uidCounter = 1000;
static uid() {
this.uidCounter += 1;
return this.uidCounter;
}
constructor(tag) {
// class name is hard coded
this.uid = Test.uid();
}
toString() {
return `${this.constructor.name} uid=${this.uid}`;
}
}
class A extends Test {
}
class B extends Test {
}
console.clear();
console.log(new A().toString());
console.log(new A().toString());
console.log(new B().toString());
console.log(new B().toString());
Who can explain, and is there a solution that doesn't depend on hard coding the parent class name?
Answering my own question:
This ticket was closed and linked to another article, even though it doesn't answer the question. I have found a solution, which I show here:
class Test {
static top = this;
static uidCounter = 1000;
static uid() {
this.uidCounter += 1;
return this.uidCounter;
}
constructor(tag) {
// instead of hard coding the class name, use an additional class variable
this.uid = this.constructor.top.uid();
}
toString() {
return `${this.constructor.name} uid=${this.uid}`;
}
}
class A extends Test {
}
class B extends Test {
}
console.clear();
console.log(new Test().toString());
console.log(new Test().toString());
console.log(new A().toString());
console.log(new A().toString());
console.log(new Test().toString());
console.log(new Test().toString());
console.log(new B().toString());
console.log(new B().toString());
Create another class variable that identifies the top of the inheritance hierarchy. This way, we solve the problem in a way that allows refactoring (e.g., changing the name of the class) without needing to find/replace.
I came up with another solution. Use an array (or an object) instead of a scalar.
class Test {
// just use an array
static uidCounter = [1000];
static uid() {
this.uidCounter[0] += 1;
return this.uidCounter;
}
constructor(tag) {
this.uid = this.constructor.uid();
}
toString() {
return `${this.constructor.name} uid=${this.uid}`;
}
}
class A extends Test {
}
class B extends Test {
}
console.clear();
console.log(new Test().toString());
console.log(new Test().toString());
console.log(new A().toString());
console.log(new A().toString());
console.log(new Test().toString());
console.log(new Test().toString());
console.log(new B().toString());
console.log(new B().toString());
I could explain why it works if anybody is interested. It's clear that the people still able to see this question don't care.
It's frustrating that this question has been closed and redirected because the target question does not address the fundamental issue here. In fact, just about everybody who replied both here an on the target question do not seem to understand the key points.
My solutions here are valuable to people who would want to create better structured Javascript, and they aren't going to see it unless this page gets opened again.