✈️ Go Up

イヨのホール

Go to Nav

クラスの静的初期化

私のブログでカテゴリーを作成・管理するコードで使用する静的初期化方法と、より正しいコードに改善した事例を紹介します。

ブログプロジェクトで、カテゴリーを管理するためにクラス構文を活用したCategory.jsを設計しました。
設計時に最も重点を置いた部分は、メンバーの露出最小化でした。 すべてのメンバーに#を付けてプライベートとして宣言し、外部から直接アクセスできないようにし、 メンバーの露出が必要な場合は、フィールドはゲッターのみを提供して書き込みを制限し、メソッドは公開メソッドに変換しました。

最初の設計

Category.jsnewキーワードを通じたインスタンス化ではなく、静的メソッドでカテゴリーインスタンスを返す構造を望みました。

import Category from '$lib/post/Category.js';

Category.getCategory("Windows") // Windowsカテゴリーインスタンスを返す

静的メソッドを通じてすでに作成されたカテゴリーインスタンスを取得するデザイン

この方式で使用するには、別途の初期化なしにインポートと同時に内部で初期化が行われる必要がありました。このとき、メソッドを外部に露出せずにインポートと同時に初期化する方法を考えた結果、

  • クラス外部に「Symbol」を宣言し、
  • クラス内部に「Symbol」をメソッド名として使用した後、
  • そのメソッドを実行して外部に露出されない初期化メソッドを実装することができました。
const symbol = Symbol('Category initialization');

export default class Category {
	static [symbol]() {
	    // ...init
	}
}

Category[symbol]();

シンボルを活用したメソッド名定義で外部アクセスを防止した初期化方式

しかし、この方式には欠点がありました。 リフレクションを使用すると初期化メソッドにアクセスでき、 これを通じて初期化が再実行される可能性がありました。 将来、この問題を防ぐための改善が必要でした。

静的初期化ブロックによる改善

Javaでは{}ブロックを通じて静的メンバーとインスタンスメンバーを初期化できます。 JavaScriptでもクラス構文に適用してコードをより簡潔にすることができます。

Note

プロトタイプは即時実行関数式、「IIFE」を使用して静的初期化ロジックを実装できます。

const symbol = Symbol('Category initialization'); // [!code --]

export default class Category {
	static [symbol]() { // [!code --]
	static { // [!code ++]
	    this.#member = data; // 静的初期化でthisキーワード使用可能 // [!code ++]
	    // ...init
	}
}

Category[symbol](); // [!code --]

静的初期化ブロックを通じたクラス内部での初期化

JavaとJavaScriptの初期化ブロックの違い

Javaでは2つの初期化ブロックを使用できます:

  • static {}、静的初期化ブロック
  • {}、オブジェクト初期化ブロック

一方、JavaScriptでは1つの初期化ブロックのみ使用できます:

  • static {}、静的初期化ブロック

さらに、JavaとJavaScriptは初期化ブロックでのthisキーワード使用に違いがあります。 Javaはインスタンス生成後にのみthisを使用できますが、 JavaScriptは第一級オブジェクトとして静的初期化ブロックでもthisを使用して静的メンバーにアクセスできます。 この違いは、JavaScriptが関数型プログラミングの特性をよりよくサポートするためです。