Search code examples
javascriptclassinstancecounteruniqueidentifier

What method should I use to count instances of subclasses in JavaScript?


I would like to implement a counter that will generate a unique ID for every instances of classes (and subclasses).

At the moment I use this:

class Item {
	constructor(type) {
		this.constructor.counter = (this.constructor.counter || 0) + 1;
		this.type = type || "item";
		this._id = this.type + "_" + this.constructor.counter;
		console.log(this.id);
	}
			
	get id() {
		return this._id;
	}
}

class SubItem extends Item {
	constructor() {
		super("sub_item");
	}
}

var test1 = new Item(); // <- "item_1"
var test2 = new Item(); // <- "item_2"
var test3 = new Item(); // <- "item_3"
var test4 = new Item(); // <- "item_4"
var test5 = new SubItem(); // <- "sub_item_5"
var test6 = new SubItem(); // <- "sub_item_6"
var test7 = new Item(); // <- "item_5"
var test8 = new Item(); // <- "item_6"

Do you see the problem? If I do it by instantiating a SubItem first, every is working correctly... But when I instantiate the "mother class" first, the counter is broken. Is there a way to solve this problem?

The behavior I am looking for is:

var test1 = new Item(); // <- "item_1" 
var test2 = new Item(); // <- "item_2" 
var test3 = new Item(); // <- "item_3" 
var test4 = new Item(); // <- "item_4" 
var test5 = new SubItem(); // <- "sub_item_1" 
var test6 = new SubItem(); // <- "sub_item_2" 
var test7 = new Item(); // <- "item_5" 
var test8 = new Item(); // <- "item_6" 

Solution

  • The problem is that the counter property on the SubItem is shadowing the counter property on the Item class. So, when the following line runs:

    this.constructor.counter = (this.constructor.counter || 0) + 1;
    

    When the first SubItem is instantiated, the this.constructor.counter that is being accessed is the counter on Item, via prototypal inheritence. But then the assignment to this.constructor.counter assigns to the counter property directly on the SubItem. (so, further instantiations of SubItem will refer to the counter directly on SubItem rather than the parent class).

    You might check to see if the counter property is directly on the constructor instead, via hasOwnProperty:

    class Item {
      constructor(type) {
        this.constructor.counter = ((this.constructor.hasOwnProperty('counter') && this.constructor.counter) || 0) + 1;
        this.type = type || "item";
        this._id = this.type + "_" + this.constructor.counter;
        console.log(this.id);
      }
    
      get id() {
        return this._id;
      }
    }
    
    class SubItem extends Item {
      constructor() {
        super("sub_item");
      }
    }
    
    var test1 = new Item(); // <- "item_1"
    var test2 = new Item(); // <- "item_2"
    var test3 = new Item(); // <- "item_3"
    var test4 = new Item(); // <- "item_4"
    var test5 = new SubItem(); // <- "sub_item_5"
    var test6 = new SubItem(); // <- "sub_item_6"
    var test7 = new Item(); // <- "item_5"
    var test8 = new Item(); // <- "item_6"