从类静态方法调用ES6类构造函数

7

我正在尝试在JS ES6类中实现单例模式。以下是我目前所写的内容:

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

然而,当我调用静态方法TestClass.getInstance()时,我得到的不是类对象的实例,而是
ƒ anonymous() {

}

我无法访问testMethod函数,但我找不到代码错误 - 非常感谢您的帮助。


instance = new TestClass(); - Keith
在JavaScript中,单例被称为对象。 - Jonas Wilms
我不想使用"new"关键字,因此如果构造函数被调用时使用了"new",我会抛出错误。@JonasW. 是的,我知道,最简单的单例是一个简单的JS对象{}。但我想使用ES6类。 - Furman
只需不导出类。只需导出执行所需操作的 getInstance() 函数。或者,更好的方法是创建并导出单例。即使您没有导出类本身,仍然可以通过一个调用在单例上调用所有方法。 - jfriend00
我不想使用new关键字,只需导出对象而不是类。export new TestClass(),如果您是为了懒惰的构造而这样做,请导出一个包装器函数来导出对象。 - Keith
3个回答

7

TestClass 是构造函数。 TestClass.constructor 是内置的 Function,当调用它时会构造一个新的空函数(您正在记录的内容)。

TestClass 构造函数也可以被访问为 TestClass.prototype.constructor,这可能是您想要的:

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

当然会抛出异常,因为你不能直接调用class构造函数而不使用new

你也可以简化为new TestClass。或者更好的做法是,如果你想支持子类,可以使用new this - 注意,在静态方法中,this指的是该类(构造函数)本身。

我正在尝试在JS ES6类中实现单例模式

请不要这样做。单例模式是不良实践。如果你的类没有任何状态,并且只有一个实例,不要使用class。只需编写:

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

如果您坚持懒惰地构建模块,我建议:
let instance;
/*default*/ export function getInstance() {
    return instance || (instance = { // use a simple object literal
        testMethod(){
            console.log('test');
        }
    });
}

话虽如此,如果您坚持要创建“私有”构造函数,我建议传递一个令牌:

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

但你实际上不应该需要那个。


是的,我知道有许多不同的方法可以实现类似的效果。ES6单例模式的实现仅限于教育目的。 - Furman
@jfriend00 普通构造函数不起作用,否则任何人都可以执行 new (getInstance().constructor)。但是,无论是导出 getInstance 还是整个类都没有太大区别(其他静态方法也是如此)。 - Bergi
有很多方法可以避免这种情况,如果这是一个问题的话。 我的观点是,OP试图做的事情以及您展示的内容比仅仅导出单例更复杂得多。 实际上,根本没有理由导出整个类。 - jfriend00
@jfriend00 是的,虽然我的个人建议是始终避免单例实例,但我更新了答案,提供了一个简单的方法来实例化单例对象。 - Bergi
好的。你的回答中有一些难以确定你认为哪个选项是简单和推荐的。 - jfriend00
显示剩余3条评论

1
你还没有创建 TestClass 的实例,你只是将 instance 变量分配为 TestClass 的构造函数。
如果需要创建单例模式,我通常会这样做:
class TestClass {
  constructor() {

  }

  testMethod() {

  }
}

const instance = new TestClass();

export default instance;

1
问题在于ES6中,类的构造函数不能在没有'new'的情况下调用 - 您的 'new.target' 测试是多余的。如果您想保留类语法,可以像以下代码一样做一些事情,以确保只有您的模块具有创建类的能力:

let instance;
let creating = false;
class TestClass{
  constructor(key) {
    if(!creating) {
      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) {
      creating = true;
      instance = new TestClass();
      creating = false;
    }
    return instance;
  }
}

const theInst = TestClass.getInstance();
theInst.testMethod();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接