Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling ES6 class constructor from class static method

I'm trying to do my implementation of singleton pattern in JS ES6 class. Here is what I wrote so far:

let instance;

export class TestClass{

    constructor(){
        if(new.target){
            throw new Error(`Can't create instance of singleton class with new keyword. Use getInstance() static method instead`);
        }
    }
    testMethod(){
        console.log('test');
    }
    static getInstance(){
        if(!instance) {
            instance = TestClass.constructor();
        }

        return instance;
    }
}

However, when I call static method TestClass.getInstance(), I'm not getting instance of class object, I'm getting

ƒ anonymous() {

}

function, without access to testMethod. I can't find error in my code - help will be greatly appreciated.

like image 504
Furman Avatar asked Apr 15 '18 20:04

Furman


2 Answers

TestClass is the constructor function. TestClass.constructor is the builtin Function, which when called constructs a new empty function (what you are logging).

The TestClass constructor can also be accessed as TestClass.prototype.constructor, that's what you probably meant:

static getInstance(){
    if (!instance) {
        instance = TestClass.prototype.constructor();
    }
    return instance;
}

This will of course throw an exception that you cannot call class constructors without new.

You also should simplify to new TestClass. Or even better, in case you want to support subclassing, new this - notice that this in static method refers to the class (constructor) itself.

I'm trying to do my implementation of singleton pattern in JS ES6 class

Please don't. Singletons are bad practice. If your class doesn't have any state, and there's just one instance anyway, don't use a class. Just write

export function testMethod() {
    console.log('test');
}
// Yes, that's the whole file!

If you insist on lazily constructing the module, I would recommend

let instance;
/*default*/ export function getInstance() {
    return instance || (instance = { // use a simple object literal
        testMethod(){
            console.log('test');
        }
    });
}

That said, if you insist on making a "private" constructor I would pass a token:

const internal = Symbol("creation token for TestClass");
export class TestClass {
    constructor(token) {
        if(token !== internal) {
            throw new Error("Please use the TestClass.getInstance() static method instead");
        }
    }
    …
    static getInstance(){
        return new TestClass(internal); // make sure not to call `this`, otherwise subclassing could leak the token
    }
}

But you should never really need that.

like image 55
Bergi Avatar answered Sep 30 '22 02:09

Bergi


You haven't created an instance of the TestClass, you've just assigned your instance variable as the constructor function of the TestClass.

I typically would create the singleton like this if I need to:

class TestClass {
  constructor() {

  }

  testMethod() {

  }
}

const instance = new TestClass();

export default instance;
like image 45
Dan D Avatar answered Sep 30 '22 03:09

Dan D