というわけでJavaScirptのクラス内にprivateメソッドやらprivateプロパティやらを実現したいな、と思って調べた時に色々知ることがあったのでまとめてみる。
主な実装方法
調べてみると4つほど方法がありました。
- privateメソッド・プロパティの接頭辞に_を付ける
- クロージャを使った実装
- Symbolを使う
- WeakMapを使う
それぞれについてうんうんと考えてみましょう。
privateメソッド・プロパティの接頭辞に_を付ける
これは超簡単ですね。
以下の様な感じ。
var Hoge = function(){}; Hoge.prototype = { publicMethod: function() { console.log('パブリックメソッドだよ!') }, _privateMethod: function() { console.log('プライベートメソッドだよ!') } } var hoge = new Hoge(); hoge.publicMethod(); hoge._privateMethod(); // プライベートメソッドなのに呼べちゃう
これはただの紳士協定で、「プライベートメソッドは接頭辞にアンダースコアを付けるので外から呼ばないでね」って感じのものです。
ゆるい。
クロージャを使った実装
これはこちらの記事にとても詳しく書いてあります。 (@jun1s様に感謝)
メモリ消費の観点から現実的では無い模様。
Symbolを使う
SymbolはES6で新たに追加されたプリミティブなデータ型ですね。
これを使って果たしてprivateを実現できるのでしょうか。
とりあえずやってみました。
class Hoge { constructor() { let privateProperty = Symbol['privateProperty'] let privateMethod = Symbol['privateMethod'] } setFoo() { this[this.privateProperty] = 'foo' } getFoo() { return this[this.privateProperty] } setBar() { this[this.privateMethod] = function() { return 'bar' } } getBar() { return this[this.privateMethod]() } } let hoge = new Hoge() hoge.setFoo() console.log(hoge.getFoo()) hoge.setBar() console.log(hoge.getBar())
これでHogeクラスにprivateプロパティ・メソッドが実現できました。
ただし、Symbolを使った方法は1点問題があり、
- そもそもprivateじゃない
という問題点があります。 どういうことでしょうか?
そもそもprivateじゃない問題
実は this[this.sym]
で設定した値を外から取得する方法があります。
それは getOwnPropertySymbols
を使うとSymbolが取得できてしまうというもの。
ただ、手元でやってみた結果Symbolが取得できませんでした・・・
何故に。
class Hoge { constructor() { let sym = Symbol['privateMethod'] } setFoo() { this[this.sym] = 'foo' } getFoo() { return this[this.sym] } } let hoge = new Hoge() hoge.setFoo() console.log(Object.getOwnPropertySymbols(hoge)) // 結果 // []
参考:
というわけでSymbolを用いた方法もちょっと違うかなと。
[追記]
@gaogao_9さんからアドバイスを頂けました。
@syossan27
— がお (@gaogao_9) August 9, 2016
const hogeSymbol = Symbol("hoge");
class {
[hogeSymbol](){
/* 処理 */
}
}
みたいな動的プロパティ指定が出来ますのでこれでprivateメソッドを簡潔に書けますよ。
ご教授頂いたコードがこちら。
const Hoge = (()=> { const privateProperty = Symbol('privateProperty') const privateMethod = Symbol('privateMethod') class Hoge { constructor() { } set foo(value){ this[privateProperty] = value } get foo(){ return this[privateProperty] } [privateMethod](){ return 'bar' } get bar() { return this[privateMethod] } } return Hoge; })(); let hoge = new Hoge() hoge.foo = 'foo' console.log(hoge.foo) console.log(hoge.bar())
こちらの方が見やすいですね!
WeakMapを使う
WeakMapというMapのキーにオブジェクトが使える方法があるので、それを使ってprivateを実現する。
const privateMap = new WeakMap() function getPrivates(self) { let p = privateMap.get(self); if (!p) { p = {}; privateMap.set(self, p); } return p; } class Hoge { constructor() { } setFoo() { getPrivates(this).foo = 'foo' } getFoo() { return getPrivates(this).foo } setBar() { getPrivates(this).bar = function() { return 'bar' } } getBar() { return getPrivates(this).bar() } } let hoge = new Hoge() hoge.setFoo() console.log(hoge.getFoo()) hoge.setBar() console.log(hoge.getBar())
WeakMapでclassをセットして、そこにprivateプロパティなりメソッドなりをセットするような感じ。
getPrivates(hoge).foo
とかで取れちゃうじゃん!って思われるかもしれませんが、実際にはclass部分を export default class
とかで切り出して、
実行部は別にするので大丈夫。
まとめ
なんとなーく薄い理解ですが、WeakMapを使えばprivateが実現できますが深いことを考えなければSymbolでもいいのかなと。
更に深いことを考えなければアンダースコア使っちゃってもいいのかなと。
この辺りは実装者によってだいぶ分かれそうですね。
なんかふんわり理解をまとめた感じなので、「これ違うだろ」というツッコミ頂けると喜びます。