在 es6 中扩展单例类

Ami*_*ner 3 javascript oop es6-class

我需要在 JavaScript 中扩展一个单例类。

我面临的问题是我获取了我所扩展的类实例,而不是仅获取了该类的方法。

我尝试删除super以获取实例,但随后出现错误

在访问“this”或从派生构造函数返回之前,必须调用派生类中的超级构造函数

代码示例:

let instanceA = null;
let instanceB = null;

class A {
  constructor(options) {
    if (instanceA === null) {
      this.options = options;
      instanceA = this;
    }
    return instanceA;
  }
}

class B extends A {
  constructor(options) {
    if (instanceB === null) {
      super()
      console.log('real class is ' + this.constructor.name)
      this.options = options
      instanceB = this;
    }
    return instanceB;
  }
}

const a = new A({
  i_am_a: "aaaaaa"
});

const b = new B({
  i_am_b: "bbbbbb"
}) // this change a 

console.log(b.options)

console.log(a.options)
Run Code Online (Sandbox Code Playgroud)

FK8*_*K82 5

所以,首先这里有一个误解:

我尝试删除 super 以不获取实例,但随后出现错误

super()在创建的子类实例上调用父类的构造函数this(即引用的内容)。它不返回父类实例。浏览此处获取更多信息。

所以,调用super()根本不违反父类的单例属性。如果正确实施,它很可能只会构建一次。


考虑到这一点,您应该稍微改进您的代码。

一个明智的改变是从构造函数中删除实例管理。一种解决方案是使用静态构造函数,它要么在不存在实例的情况下创建单例,要么返回创建的实例。

另一种方法是删除单例类构造函数的参数。将参数传递给一个应该实例化一次的类并没有真正的意义(你永远不会再对构造函数参数做任何事情)。您可以立即设置单例的参数属性。这是支持 Java 单例这一点的SO 答案。

具有静态构造函数且不带参数的完整示例如下所示:

let instanceA = null;
let instanceB = null;

let counters = { A: 0, B: 0 }; // count class instantiations

class A {
  static getInstance() {
    if (instanceA === null) {
      instanceA = new A();
    }
    return instanceA;
  }
  whoami() {
    const name = this.constructor.name;
    return `${name} #${counters[name]}`;
  }
  constructor() {
    counters[this.constructor.name] += 1;
  }
}

class B extends A {
  static getInstance() {
    if (instanceB === null) {
      instanceB = new B();
    }
    return instanceB;
  }
  constructor() {
    super();
  }
}

const a1 = A.getInstance();
const a2 = A.getInstance();
const a3 = A.getInstance();
const b1 = B.getInstance();
const b2 = B.getInstance();
const b3 = B.getInstance();
console.log(a1.whoami());
console.log(a2.whoami());
console.log(a3.whoami());
console.log(b1.whoami());
console.log(b2.whoami());
console.log(b3.whoami());
Run Code Online (Sandbox Code Playgroud)

请注意,B继承whoamiA且构造函数调用计数器永远不会增加到超过 1。

显然,使用这种方法,您不能保证单例属性适用于每个类,除非仅使用静态构造函数来生成实例(因为构造函数仍然可以访问)。我认为这是一个很好的妥协。