【问题标题】:Calling ES6 class constructor from class static method从类静态方法调用 ES6 类构造函数
【发布时间】:2018-09-25 13:17:10
【问题描述】:

我正在尝试在 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();
  • javascript中的单例称为object。
  • 我不想使用 new 关键字,这就是为什么如果使用“new”调用构造函数,我会在构造函数中抛出错误。 @乔纳斯W。是的,我知道,最简单的单例就是简单的 JS 对象 {}。我想使用 ES6 类。
  • 只是不要导出类。只导出你想要的 getInstance() 函数。或者,也许更好,只是创建和导出单例,仅此而已。即使您没有导出类本身,一个调用仍会调用单例上的所有方法。
  • I don't want to use new keyword ,然后只导出一个对象而不是类。 export new TestClass() ,如果你这样做是为了惰性构造,那么导出一个包装函数来导出对象。

标签: javascript constructor ecmascript-6 singleton es6-class


【解决方案1】:

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

TestClass 构造函数也可以作为TestClass.prototype.constructor 访问,这可能是您的意思:

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

这当然会抛出一个异常,你不能在没有new 的情况下调用class 构造函数。

您还应该简化为new TestClass。或者更好的是,如果您想支持子类化,new this - 请注意静态方法中的 this 指的是类(构造函数)本身。

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

请不要。单身人士是不好的做法。如果您的班级没有任何状态,并且无论如何只有一个实例,don't use a 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
    }
}

但你永远不应该真正需要它。

【讨论】:

  • 所以不使用“new”关键字就无法创建ES6类对象?
  • 这是一个非常棒的符号令牌技巧 - 感谢您的详细回答。
  • 为什么还要导出类呢?为什么不只导出一个返回单例的工厂函数。然后,构造函数可以只是一个普通的构造函数,仅在模块内用于创建原始单例。公众调用总是返回单例的工厂函数。
  • 是的,我知道有很多不同的方法可以达到类似的效果。 ES6 单例实现仅用于教育目的。
  • @jfriend00 普通构造函数不起作用,否则任何人都可以做到new (getInstance().constructor)。但是,是的,getInstance 是导出还是整个类都没有太大区别(其他静态方法也是如此)。
【解决方案2】:

您尚未创建 TestClass 的实例,您只是将 instance 变量分配为 TestClass 的构造函数。

如果需要,我通常会像这样创建单例:

class TestClass {
  constructor() {

  }

  testMethod() {

  }
}

const instance = new TestClass();

export default instance;

【讨论】:

    【解决方案3】:

    问题在于 ES6 Class constructors cannot be invoked without 'new' - 你的 new.target 测试是多余的。如果您想保留 Class 语法,您可以执行以下操作以确保只有您的模块能够创建类:

    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();

    【讨论】:

      猜你喜欢
      • 2016-08-28
      • 1970-01-01
      • 2017-04-07
      • 1970-01-01
      • 2011-03-24
      • 1970-01-01
      • 2021-02-03
      相关资源
      最近更新 更多