静的初期化ブロック

静的初期化ブロッククラス内で宣言されます。これは、クラスの初期化の時に評価される文を格納します。これにより、静的プロパティよりも柔軟な初期化ロジックが可能になり、 try...catch を使用したり、 1 つの値から複数のフィールドを設定したりすることができます。初期化は現在のクラス宣言のコンテキストで実行され、プライベートプロパティにアクセスすることができます。つまり、インスタンスのプライベートフィールドを持つクラスと、同じスコープで宣言された他のクラスや関数との間で情報を共有するためにも使用できます(C++ の "friend" クラスに似ています)。

試してみましょう

構文

js
class ClassWithSIB {
  static {
    // …
  }
}

解説

静的初期化ブロックを使わない場合、クラス宣言の後で静的メソッドを呼び出すことで、複雑な静的初期化を行うことができます。

js
class MyClass {
  static init() {
    // プライベート静的フィールドにアクセスすることができる
  }
}

MyClass.init();

しかし、この手法では実装の詳細(init() メソッド)がクラスのユーザーに公開されてしまいます。一方、クラスの外部で宣言された初期化ロジックはプライベート静的フィールドにアクセスすることはできません。静的初期化ブロックでは、任意の初期化ロジックをクラス内で宣言し、クラスの評価中に実行することができます。

class は、そのクラス本体に任意の数の static {} 初期化ブロックを置くことができます。 これらのブロックは、宣言された順に、静的フィールド初期化子とともに評価されます。 スーパークラスの静的初期化は、そのサブクラスの初期化よりも先に実行されます。

静的ブロックの内部で宣言された変数のスコープは、そのブロックのローカルなものです。ここには初期化ブロック内で宣言された var, function, const, let は、そのブロックのローカル変数であるため、 var 宣言は静的ブロックの外に巻き上げされることはありません。

js
var y = "Outer y";

class A {
  static field = "Inner y";
  static {
    // var y はブロックの中にしか巻き上げられない
    console.log(y); // undefined <-- not 'Outer y'

    var y = this.field;
  }
}

// 静的ブロックで定義された var y は
// ブロックの外に巻き上げられない
console.log(y); // 'Outer y'

静的ブロック内の this は、そのクラスのコンストラクター オブジェクトを参照します。 super.プロパティ を使用して、スーパークラスの静的プロパティにアクセスすることができます。 ただし、クラスの静的初期化ブロック内で super() を呼び出したり、 arguments オブジェクトを使用したりするのは構文エラーであることに注意してください。

式は同期的に評価されます。初期化子式で(awaityield)を使用することはできません。(初期化子式は暗黙に関数に包まれていると考えてください)。

静的ブロックのスコープは、クラス本体の字句スコープの中で入れ子になり、構文エラーを発生させることなく、クラス内で宣言されたプライベート名にアクセスすることができます。

静的フィールド初期化子と静的初期化ブロックは、 1 つずつ評価されます。フィールド初期化子は、それより上のフィールド値を参照することはできますが、それより下のフィールド値を参照することはできません。静的メソッドはすべて事前に追加され、アクセスすることができますが、初期化されるフィールドより下のフィールドを参照している場合、呼び出すと期待した動作をしないことがあります。

メモ: これはプライベート静的フィールドではより重要です。初期化されていないプライベートフィールドにアクセスすると、たとえそのプライベートフィールドが下で宣言されていたとしても、 TypeError が発生するからです。(プライベートフィールドが宣言されていない場合は、早期に SyntaxError となります。)

静的初期化ブロックはデコレーターを持つことができません(クラス自身は持つことができます)。

複数のブロック

下記コードは、静的初期化ブロックと静的フィールド初期化子を挟み込んだクラスを示すものです。 出力は、ブロックとフィールドが実行順に評価されることを示しています。

js
class MyClass {
  static field1 = console.log("static field1");
  static {
    console.log("static block1");
  }
  static field2 = console.log("static field2");
  static {
    console.log("static block2");
  }
}
// 'static field1'
// 'static block1'
// 'static field2'
// 'static block2'

スーパークラスの静的な初期化は、サブクラスの初期化よりも最初に行われることに注意してください。

this と super の使用

静的ブロック内の this は、そのクラスのコンストラクター オブジェクトを参照します。 このコードは、パブリック静的フィールドにアクセスする方法を示しています。

js
class A {
  static field = "static field";
  static {
    console.log(this.field);
  }
}
// 'static field'

super.property 構文を static ブロックの中で使用すると、スーパークラスの静的プロパティを参照することができます。

js
class A {
  static field = "static field";
}

class B extends A {
  static {
    console.log(super.field);
  }
}
// 'static field'

プライベートプロパティへのアクセス

下記は、クラス外のオブジェクトからクラスのプライベートインスタンスフィールドにアクセスを許可する例です(v8.dev blogより)。

js
let getDPrivateField;

class D {
  #privateField;
  constructor(v) {
    this.#privateField = v;
  }
  static {
    getDPrivateField = (d) => d.#privateField;
  }
}

console.log(getDPrivateField(new D("private"))); // 'private'

仕様書

Specification
ECMAScript Language Specification
# prod-ClassStaticBlock

ブラウザーの互換性

BCD tables only load in the browser

関連情報