JavaScriptにも一応classの概念があります。
でも次のような制約がありました。
- privateな変数(フィールド)が作れない
- privateな関数(メソッド)も作れない
- プロパティは全てpublicになる
まあJSのclassは糖衣構文的なので仕方ないですが
でもプライベート(private)な変数・関数を作るのは可能!
自分もつい最近知って関心したので、
プライベート変数・関数の作り方をまとめます。
このページの目次
まず必要知識である Symbol について
JSには Symbol というデータ型が存在します。
▼ Symbolとは何なのか?
データ型 symbol は、プリミティブデータ型です。Symbol() 関数は、symbol 型の値を返します。これは組み込みオブジェクトを公開するための静的プロパティを持ち、グローバルシンボルレジストリを公開するための静的メソッドを持つので、組み込みオブジェクトクラスのようにも見えますが、コンストラクターとしての機能を持たず、"new Symbol()" はサポートされていません。
Symbol() から返されるすべてのシンボル値は一意です。シンボル値は、オブジェクトプロパティの識別子として使用できます。これがデータ型の主な利用目的ですが、不透明なデータ型の有効化や、実装サポートされている一意の識別子として機能するなど、他の利用目的も存在します。
引用元 : https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol
・・・?????
とりあえずプリミティブ型なので new Symbol() はできない、プロパティつまり変数・関数の識別子として使えるということは分かりました。
実際にコードを書けば少し理解できます。
例えばこういう風に使えます。
▼ Symbolの簡単なコード例
1 2 3 4 5 |
let id1 = Symbol('id'); let id2 = Symbol('id'); console.log(id1 === id2); /// => false |
このように同じ名前で宣言しても Symbol には 'id' みたいにタグがつけられます。でも同じタグを持ってても各Symbolは一意なので一致することはありません。
▼ そのためプロパティ隠ぺいに使える
1 2 3 4 5 6 7 8 9 10 11 12 13 |
let id = Symbol("id"); let user = { [id]: 12345678, name: 'Pisuke' }; for (let key in user){ console.log(key); /// Pisuke のみ出力される } console.log( "ID : " + user[id] ); /// ID : 12345678 |
このように user.id ではアクセスできない要素が作れるわけです。ちなみに上コードだと user[id] というようにSymbol変数がないと絶対にアクセスできません。
これもっと厳密にすればプライベート変数・関数が作れます。
privateな変数・関数をSymbolで作ってみる
ではclassにprivateな変数・関数を追加
実際にこのようなコードを書いてみました。
▼ ユーザーのID・名前だけ管理するクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const User = (function(){ /// プライベート用の識別子 const _id = Symbol(); const _setId = Symbol(); return class User{ constructor(id, name){ this[_setId](id); } /// publisなメソッド getId(){ return this[_id]; } /// privateなメソッド [_setId](id){ /// private変数に代入 this[_id] = id; } }; })(); var user = new User(123, 'Pisuke') console.log(user.getId()) /// => 123 console.log(user._id) /// => undefined |
まず const User = (function(){....})() のように囲むのがポイント。こうすることで const _id = Symbol() みたいにプライベート変数・関数用の識別子を宣言できます。
そして変数なら this[_id] のように、メソッドだったら this[_setId](id) のような感じで、完全にクラス内部からしか参照・呼び出しができなくなります。
当然外部から user._id = -1 とかしたり、 user._setId(-1) したりするのは無理。どう頑張っても内部のSymbol変数は知りえないので不可能です。
もし「この方法にも穴があるよ!」「外部からアクセスできるよ1」と突っ込みを入れたい人はコメントからご指摘ください。(多分いないだろうけど...)
ちなみにSymbolのブラウザの対応状況について
こんな便利な方法ですが1つ懸念点が
それはブラウザの対応状況についてです。
一応 Can i use... で調べてみました。
▼ Symbolの各ブラウザの対応状況
IEは残念ながら(?)どのバージョンでも未対応。
でも他ブラウザならモバイルですら対応してますね。Symbolを使ったprivate変数・関数は問題なく使えます。(ただしIEと一部少数ブラウザは未対応。でも問題なし)
JavaScriptでも疑似的にprivate変数は作れる
本当は言語的にサポートされるべきですが・・・
ちょっとしてトリックでprivateも再現できます。
ただ1つ注意点があるとすれば、これは厳密な方法ではなく疑似的なものということですね。JavaScriptは本当のオブジェクト指向でないことに注意です。
以上、JSでプライベート変数・関数でした。ではまた