プライベートクラス機能

クラスのフィールドは既定で公開ですが、ハッシュ # 接頭辞を使ってプライベートクラスメンバーを生成することができます。これらのクラス機能のプライバシーのカプセル化は、JavaScript 自身によって行われます。

構文

class ClassWithPrivateField {
  #privateField;
}

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }
}

class ClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;
}

class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return 'hello world';
  }
}

プライベートインスタンスフィールド

プライベートインスタンスフィールドは # 名 (ハッシュ名と読む) で宣言され、これは # の接頭辞が付いた識別子です。この # は名前の一部で、宣言やアクセスにも使われます。プライベートフィールドはクラスのコンストラクター上でクラス定義自身の中から参照できます。フィールド名の宣言や、フィールドの値へのアクセスに使用されます。

スコープ外から # 名を参照すると構文エラーになります。また、呼び出される前に宣言されていないプライベートフィールドを参照したり、宣言されているフィールドを delete で削除しようとしても構文エラーになります。

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    delete this.#privateField;   // Syntax error
    this.#undeclaredField = 444; // Syntax error
  }
}

const instance = new ClassWithPrivateField()
instance.#privateField === 42;   // Syntax error

メモ: in 演算子を使用すると、プライベートフィールド (またはプライベートメソッド) が欠けているかどうかをチェックできます。そのプライベートフィールドが存在すれば true を返し、そうでなければ false を返します。

パブリックフィールドのように、プライベートフィールドは基底クラスの構築辞典、またはサブクラス内で super() が呼び出される場面で追加されます。

class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
  }
}

class SubClass extends ClassWithPrivateField {
  #subPrivateField;

  constructor() {
    super();
    this.#subPrivateField = 23;
  }
}

new SubClass();
// SubClass {#privateField: 42, #subPrivateField: 23}

プライベート静的フィールド

プライベートフィールドは、クラスのコンストラクター上でクラス宣言の内部からアクセスできます。 静的変数は静的メソッドからのみ呼び出せるという制限はまだあります。

class ClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static publicStaticMethod() {
    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
  }
}

console.log(ClassWithPrivateStaticField.publicStaticMethod() === 42);
// true

プライベート静的フィールドには制限があります。プライベート静的フィールドを定義したクラスのみが、そのフィールドにアクセスできます。this を使用すると、予期しない動作をする可能性があります。 次の例では、SubClass.basePublicStaticMethod() を呼び出そうとしたときに、これが (BaseClassWithPrivateStaticField クラスではなく) SubClass クラスを参照してしまい、 TypeError が発生します。

class BaseClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static basePublicStaticMethod() {
    this.#PRIVATE_STATIC_FIELD = 42;
    return this.#PRIVATE_STATIC_FIELD;
  }
}

class SubClass extends BaseClassWithPrivateStaticField { };

let error = null;

try {
  SubClass.basePublicStaticMethod()
} catch(e) { error = e};

console.log(error instanceof TypeError);
// true
console.log(error);
// TypeError: Cannot write private member #PRIVATE_STATIC_FIELD
// to an object whose class did not declare it

プライベートメソッド

プライベートインスタンスメソッド

プライベートインスタンスメソッドは、プライベートインスタンスフィールドと同様にアクセスが制限されているクラスインスタンスで利用できるメソッドです。

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
    return this.#privateMethod();
  }
}

const instance = new ClassWithPrivateMethod();
console.log(instance.getPrivateMessage());
// hello world

プライベートインスタンスメソッドは、ジェネレーター関数、非同期関数、非同期ジェネレーター関数のいずれかになります。プライベートなゲッターやセッターも可能です。

class ClassWithPrivateAccessor {
  #message;

  get #decoratedMessage() {
    return `🎬${this.#message}🛑`;
  }
  set #decoratedMessage(msg) {
    this.#message = msg;
  }

  constructor() {
    this.#decoratedMessage = 'hello world';
    console.log(this.#decoratedMessage);
  }
}

new ClassWithPrivateAccessor();
// 🎬hello world🛑

プライベート静的メソッド

プライベート静的メソッドは、パブリックと同様に、クラスのインスタンスではなく、クラス自体から呼び出されます。プライベート静的フィールドと同様に、クラス宣言の内部からのみアクセス可能です。

class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return 42;
  }

  static publicStaticMethod1() {
    return ClassWithPrivateStaticMethod.#privateStaticMethod();
  }

  static publicStaticMethod2() {
    return this.#privateStaticMethod();
  }
}

console.log(ClassWithPrivateStaticMethod.publicStaticMethod1() === 42);
// true
console.log(ClassWithPrivateStaticMethod.publicStaticMethod2() === 42);
// true

プライベート静的メソッドは、ジェネレーター関数、非同期関数、非同期ジェネレーター関数、などがあります。

前述のプライベート静的フィールドの制限は、プライベート静的メソッドにも当てはまり、this を同様に使用すると予期しない動作が発生する可能性があります。次の例では、 Derived.publicStaticMethod2() を呼び出そうとすると、これは Base クラスではなく Derived クラスを参照するため、TypeError が発生します。

class Base {
  static #privateStaticMethod() {
    return 42;
  }
  static publicStaticMethod1() {
    return Base.#privateStaticMethod();
  }
  static publicStaticMethod2() {
    return this.#privateStaticMethod();
  }
}

class Derived extends Base {}

console.log(Derived.publicStaticMethod1());
// 42
console.log(Derived.publicStaticMethod2());
// TypeError: Cannot read private member #privateStaticMethod
// from an object whose class did not declare it

仕様書

Specification
ECMAScript Language Specification
# sec-class-definitions

ブラウザーの互換性

BCD tables only load in the browser

関連情報