0

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.

9
  • the first example runs perfectly (NodeJS v14.5.0) Commented Oct 21, 2020 at 0:57
  • "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 when you use the refactoring feature of your IDE to change the name everywhere the variable is referenced. Especially inside the class itself, this is a no-brainer. "is there a solution that doesn't depend on hard coding the parent class name?" - no. Commented Oct 21, 2020 at 1:05
  • @RandyCasburn Runs without errors maybe, but doesn't have the desired results. Commented Oct 21, 2020 at 1:06
  • @RandyCasburn , I just tried the first example in NodeJS v14.5.0, and it fails in the same way as I documented above. Could you please try again? Commented Oct 21, 2020 at 6:01
  • @Bergi, respectfully, I disagree. It's better to write code in a way that allows you to make changes in a concise manner. Think, for example, about what happens with your version tracking if you have to make the changes in 30 different places? That's much harder for somebody to digest than if there is a change in a single place. The reader is going to have to scan 30 different changes to confirm that they are all the same change. As a fallback, sure, use the IDE, but a more elegant solution is better, if it worked. Hence my question. Commented Oct 21, 2020 at 6:04

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.