How can a static method be called dynamically from an instance method?

30 Views Asked by At

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;
    }
}
0

There are 0 best solutions below