JavaScriptのclassのインスタンス生成を1つしか許可しない方法

classのインスタンス生成を1つしか許可しない

JavaScriptでは、classを使うと複数のインスタンスを生成することができます。

JavaScript
class Counter {
  private counter = 0
  
  increment() {
    return ++this.counter
  }

  getCount() {
    return this.counter
  }
}

const myCounter = new Counter()
console.log(myCounter.increment())
console.log(myCounter.getCount())

const iCounter = new Counter()
console.log(iCounter.increment())
console.log(iCounter.getCount())

しかし、classによっては、2つ目のインスタンス生成を許可したくないケースもあります。

そんなときは「Counter.instance = this」でclass内にインスタンスを保存しておけば、すでにインスタンスが存在する場合は、new Errorでエラーを返すと良いです。

TypeScript
class Counter {
  private static instance: Counter | null = null
  private counter = 0

  constructor() {
    if (Counter.instance) {
      throw new Error('Counterの1つ目のインスタンスを生成済みです。')
    }
    Counter.instance = this
  }

  increment() {
    return ++this.counter
  }

  getCount() {
    return this.counter
  }
}

const myCounter = new Counter()
console.log(myCounter.increment())
console.log(myCounter.getCount())

const iCounter = new Counter()
// Uncaught Error: Counterの1つ目のインスタンスを生成済みです。

functionだとclassのprivate static instanceのように内部に状態を設定して保持できないので、外側にletで宣言が必要になってしまいます。

JavaScript
let counterInstance = false

function Counter() {
  let counter = 0

  if (counterInstance) {
    throw new Error('Counterの1つ目のインスタンスを生成済みです。')
  }

  counterInstance = true

  return {
    increment() {
      return ++counter
    },
    getCount() {
      return counter
    }
  }
}

const myCounter = Counter()
console.log(myCounter.increment())
console.log(myCounter.getCount())

const iCounter = Counter()
// Uncaught Error: Counterの1つ目のインスタンスを生成済みです。

※ Counter.instance = true のように設定もできるが、VS Codeだとエラーが表示されてしまう。

ちなみにclassをfunctionに書き換えると、長いコードの場合は書き間違いや想定外のバグが発生することもあるので注意が必要です。