ES2015 の Class で private なインスタンス変数


Symbols を利用します。例えば Person クラスで「名前」「年齢」を private にしたい場合は次のようにします。

person.es6
const propName = Symbol();
const propAge = Symbol();

/**
 * Person class
 */
class Person {
  /**
   * @constructor
   * @param {string} name
   * @param {number} age
   */
  constructor(name, age) {
    this[propName] = name;
    this[propAge] = age;
  }
}

export default Person;

変数 propName および propAage は一意であり、他の方法で同値のものを生成できず、アクセスできるのはこのファイル内のみです。

この例では、インスタンス生成時に「名前」「年齢」をセットした後は、

some.es6
import Person from './person.es6';

const person = new Person('Yamada', 45);

「名前」「年齢」にアクセスすることはできません。

「名前」「年齢」を Person クラス内のメソッドのみで利用する場合はこれでいいのですが、外部から参照だけしたい場合は Getter を用意すればいいです。

person.es6
const propName = Symbol();
const propAge = Symbol();

/**
 * Person class
 */
class Person {
  /**
   * @constructor
   * @param {string} name
   * @param {number} age
   */
  constructor(name, age) {
    this[propName] = name;
    this[propAge] = age;
  }

  /**
   * @returns {string}
   */
  get name() {
    return this[propName];
  }
}

export default Person;
some.es6
import Person from './person.es6';

const person = new Person('Yamada', 45);

console.log(person.name); // Yamada が出力される

もちろん Setter を用意することもできます。下記の例だと渡された値をそのままセットしてるので private にしている意味がありませんが、本当は値チェックを挟むなど。

person.es6
const propName = Symbol();
const propAge = Symbol();

/**
 * Person class
 */
class Person {
  /**
   * @constructor
   * @param {string} name
   * @param {number} age
   */
  constructor(name, age) {
    this[propName] = name;
    this[propAge] = age;
  }

  /**
   * @returns {string}
   */
  get name() {
    return this[propName];
  }

  /**
   * @param {string} name
   * @returns {void}
   */
  set name(name) {
    this[propName] = name;
  }
}

export default Person;

おまけ:private メソッド

コンストラクタ内でメソッド定義しないといけないので微妙ですが.. もしその private メソッドがインスタンス変数にアクセスする必要がない場合は、クラスの外で定義すればよいかもしれません。

const propName = Symbol();
const propAge = Symbol();
const propSomePrivateMethod = Symbol();

/**
 * Person class
 */
class Person {
  /**
   * @constructor
   * @param {string} name
   * @param {number} age
   */
  constructor(name, age) {
    this[propName] = name;
    this[propAge] = age;
    this[propSomePrivateMethod] = () => {
      console.log(`こんにちは、${this[propName]}さん。`);
    }
  }

  somePublicMethod() {
    // ...

    // private メソッドの呼び出し
    this[propSomePrivateMethod]();
  }
}

export default Person;

ほか余談

  • 厳密にいうと Symbols を利用したとしても、イテレータか何か(忘れました..)を利用してインスタンスのプロパティにアクセスする手段があったような気がするので、完全に private ではありません。が、そこまで気にしなくてもよいかなという感じです。
  • WebStorm 等の IDE を利用している場合は @private アノテーションをつけておけば、外部から利用しようとすると IDE 上で警告を表示してくれます。

    • 無理して Symbols を利用して煩雑になるよりも、現実的にはこれで十分かもしれません^^; メソッドにも @private アノテーションを付与することができますし。
    person.es6
    /**
     * Person class
     */
    class Person {
      /**
       * @constructor
       * @param {string} name
       * @param {number} age
       */
      constructor(name, age) {
        /** @private */
        this.name = name;
        /** @private */
        this.age = age;
      }
    }
    
    export default Person;
    

  • 素直に TypeScript を使うのがよいかも..