1
1

More than 3 years have passed since last update.

[JavaScript] Symbolによるprivateを現実的にする

Last updated at Posted at 2020-05-17

はじめに

#を付ける方法は、対応ブラウザが少ないため、#を使用せずにprivateを実現する方法として、こちらの記事を参考にさせて頂きました。
JavaScriptでprivate「Symbol」編

問題点

  • クラスを定義する前にprivateなプロパティ名を列挙しなければならない
  • publicとprivateで書き方がだいぶ違う
  • クラス作るたびにアロー関数作ってシンボル作ってとやることが多い
  • 完全なprivateじゃない

上記の記事では、これらの問題点が挙げられていました。

解決策

これらの問題点は「完全なprivateじゃない」を除き

  • Symbolの保持
  • 保持したSymbolの参照

これらの書き方が異なることに起因しています。
つまり、これらの書き方を1つに統一することが出来れば、問題は解決すると言えます。

「完全なprivateじゃない」については

delete Object.getOwnPropertySymbols

このように記述しておくことで、Hard privateを実現出来なくは無いので特に問題はありません。

シンプルなSymbol処理

const PrivateModifier = function() {
  return new Proxy({}, {
    get(obj, name) {
      if (obj[name] === undefined) {
        obj[name] = Symbol(name)
      }
      return obj[name]
    },
    set() {
      throw TypeError("PrivateModifier is read only.")
    }
  })
}

解説

通常、objectは存在しないプロパティにアクセスした場合、undefinedを返します。
Proxyを利用することで、デフォルトの挙動を変更出来ます。

PrivateModifierは、存在しないプロパティにアクセスした際、Symbolを動的に設定するobjectを作成します。

例えば

const _ = {}
_.hoge = Symbol("hoge")
console.log(_.hoge) // Symbol(hoge)
_.huga = Symbol("huga")
console.log(_.huga) // Symbol(huga)
const _ = new PrivateModifier
console.log(_.hoge) // Symbol(hoge)
console.log(_.huga) // Symbol(huga)

これらは同じ意味になります。

値はこのようになります。

const _ = new PrivateModifier
console.log(_.hoge === _.hoge) // true

使用方法

パターン1 (prototype)

const ClassName = (function() {
  const _ = new PrivateModifier

  // constructor
  function ClassName(privateValue, publicValue) {
    // private instance
    this[_.privatePropertyName] = privateValue
    // public instance
    this.publicPropertyName = publicValue
  }

  // alias
  const proto = ClassName.prototype

  // private prototype
  proto[_.privateMethodName] = function() { }
  // public prototype
  proto.publicMethodName = function() { }

  return ClassName
})()

パターン2 (class)

const ClassName = (() => {
  const _ = new PrivateModifier

  return class ClassName {
    constructor(privateValue, publicValue) {
      // private instance
      this[_.privatePropertyName] = privateValue
      // public instance
      this.publicPropertyName = publicValue
    }

    // private prototype
    ;[_.privateMethodName]() { }
    // public prototype
    publicMethodName() { }
  }
})()

対応状況

  • IE以外のブラウザで使用できます
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1