I have several classes, each of which should be able to retrieve an existing instance of itself (using an ID) when possible, rather than creating a new instance. For encapsulation and convenience, I want to store each class’s library as a static property of the class, and abstract the whole pattern for quick reuse. But I can’t find a clean way to dynamically access a static method from an instance scope in JS.
class HasLibrary
{
static library = new Map();
static retrieve(id) { return new this(id); }
constructor(id) {
let existing = HasLibrary.library.get(id);
if (existing) return existing;
this.id = id;
HasLibrary.set(id, this);
return false; // for inheritance
}
}
class Tag extends HasLibrary
{
constructor(name, options) {
let existing = super(name);
if (existing) return existing;
// do Tag-specific stuff
return this;
}
}
class Product extends HasLibrary
{
constructor(name, options) {
let existing = super(name);
if (existing) return existing;
// do Product-specific stuff
return this;
}
}
The static retrieve() function inherits correctly, so I can call Tag.retrieve("spruce") and it returns a Tag instance. (In OOP terms, I can access instance methods dynamically from static methods.) But of course, when Tag calls super(), the library property is hard-coded to the base class.
If this were (modern) PHP, I could call the static keyword to get the current class’s property.
abstract class HasLibrary
{
...
function __construct($id) {
$result = static::$library[$id];
...
I’ve resorted to setting an instance property that each of the child classes must override, but there’s still more copy-paste action required than I would prefer. (In addition, the static library property does not create new Maps when inherited, so that Tag.library and Product.library point to the same Map.)
class HasLibrary
{
static retrieve(id) { return new this(id); }
id = '';
constructor(id) {
let existing = this.self.library.get(id);
if (existing) return existing;
this.id = id;
this.self.library.set(id, this);
return false; // for inheritance
}
}
class Tag extends HasLibrary
{
static library = new Map();
get self() { return Tag; }
constructor(name, options) {
let existing = super(name);
if (existing) return existing;
// do Tag-specific stuff
return this;
}
}
class Product extends HasLibrary
{
static library = new Map();
get self() { return Product; }
constructor(name, options) {
let existing = super(name);
if (existing) return existing;
// do Product-specific stuff
return this;
}
}