1.クラスコンストラクターのデータストレージ。
2.命名規則(たとえば、プレフィックスアンダースコア)を使用してプライベートプロパティをマークします。
3. WeakMapsでのプライベートデータの保存。
4.プライベートプロパティのキー形式の文字の使用。
最初と2番目のアプローチはES5で広く使用され、3番目と4番目のアプローチはES6でのみ登場しました。 一度に1つずつ見てみましょう。
1.クラスコンストラクターでのデータストレージ
現在の例はCountdownクラスで、カウンターカウンターがゼロになったときにアクション関数を呼び出します。 この場合、カウンターとアクションはプライベート変数として保存する必要があります。
まず、コンストラクタークラスのコンテキストにアクションとカウンターを格納します。 コンテキストは、新しいスコープが実装されたときに(たとえば、関数またはコンストラクターを呼び出して)存在するパラメーターとローカル変数をJavaScriptエンジンが格納する内部データ構造です。 コードは次のとおりです。
class Countdown { constructor(counter, action) { Object.assign(this, { dec() { if (counter < 1) return; counter--; if (counter === 0) { action(); } } }); } }
カウントダウンの使用方法は次のとおりです。
> let c = new Countdown(2, () => console.log('DONE')); > c.dec(); > c.dec(); DONE
利点:
●個人データは完全に安全です。
●プライベートプロパティの名前は、親クラスおよび子クラスの他のプライベートプロパティの名前と競合しません。
短所:
●コンストラクターですべてのインスタンスメソッド(少なくともプライベートデータへのアクセスが必要なメソッド)を定義する必要があるため、コードはエレガントではなくなります。
●これが、コードが大量のメモリを消費する理由です。 プロトタイプメソッドが使用された場合、それらは配布されます。
このアプローチの詳細については、Speaking JavaScriptの「 コンストラクターの環境内のプライベートデータ(Crockford Privacy Pattern)」セクションを参照してください。
2.命名規則によるプライベートプロパティのラベル付け
次のコードは、アンダースコアがプレフィックスのプロパティにプライベートデータを保存します。
class Countdown { constructor(counter, action) { this._counter = counter; this._action = action; } dec() { if (this._counter < 1) return; this._counter--; if (this._counter === 0) { this._action(); } } }
利点:
●コードは美しく見えます。
●プロトタイプメソッドを使用できます。
短所:
●安全でない。 これは、クライアントコードの単なる指示です。
●プライベートプロパティ名が競合する場合があります。
3. WeakMapsでのプライベートデータの保存
この方法は、1番目と2番目のアプローチの利点、つまり安全性とプロトタイプメソッドの使用可能性を組み合わせたものです。 WeakMaps _counterおよび_actionは、プライベートデータを保存するために使用されます。
let _counter = new WeakMap(); let _action = new WeakMap(); class Countdown { constructor(counter, action) { _counter.set(this, counter); _action.set(this, action); } dec() { let counter = _counter.get(this); if (counter < 1) return; counter--; _counter.set(this, counter); if (counter === 0) { _action.get(this)(); } } }
変数_counterおよび_actionは、オブジェクトをプライベートデータと一致させます。 WeakMapsの動作に基づいて、オブジェクトはガベージコレクターによって削除できます。 WeakMapsが非表示になっている限り、プライベートデータは安全です。 自分自身を保護するために、WeakMap.prototype.getおよびWeakMap.prototype.setを一時変数に保存し、メソッドを動的に呼び出す代わりにそれらを呼び出すこともできます。 悪意のあるコードがこれらのメソッドをプライベートデータにアクセスできるメソッドに置き換えても、これはコードに影響しません。 ただし、保護は、私たちのコードの後に起動されたコードにのみ適用されます。残念ながら、その前に起動されたコードを保護することはできません。
利点:
●プロトタイプメソッドを使用できます。
●プロパティキーの命名規則より安全です。
●プライベートプロパティ名は競合できません。
欠点:
●コードは、命名規則ほどエレガントではありません。
4.プライベートプロパティのキーとしての文字の使用
プライベートデータウェアハウスの別の場所は、文字の形式のキーを持つプロパティです。
const _counter = Symbol('counter'); const _action = Symbol('action'); class Countdown { constructor(counter, action) { this[_counter] = counter; this[_action] = action; } dec() { if (this[_counter] < 1) return; this[_counter]--; if (this[_counter] === 0) { this[_action](); } } }
各シンボルは一意であるため、シンボルキーを持つプロパティが別のプロパティと競合することはありません。 さらに、キャラクターは完全ではありませんが、外部の影響から隠されています。
let c = new Countdown(2, () => console.log('DONE')); console.log(Object.keys(c)); // [] console.log(Reflect.ownKeys(c)); // [ Symbol(counter), Symbol(action) ]
利点:
●プロトタイプメソッドを使用できます。
●プライベートプロパティ名は競合できません。
短所:
•命名規則と比較すると、コードはそれほどエレガントではありません。
•シンボルを含むオブジェクトのすべてのプロパティキーは、Reflect.ownKeys()を使用して決定できるため、安全ではありません。
5.さらに読む:
● スピーキング。 JavaScript、データの非公開のセクション。
●クラスの責任者であるES6の探索 。
●SymbolsのヘッドであるES6の探索 。