jkr_2255
@jkr_2255

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

JavaScriptで、プライベートな値の保管方法について

解決したいこと

以下の3つの条件を満たせるような、JavaScriptのインスタンスに紐づく値の保管方法は、自分が提示した以外にないでしょうか。

  1. prototypeからもアクセスできる(必須)…元のコードがnew以外で生成したオブジェクトのプロトタイプを差し替えるなど黒魔術満載のものなので、その枠内で動くものでないといけない状況です。
  2. プライベートな値とする(できれば)…インスタンス、クロージャ、モジュールなど何かしらの単位で値を封じきって、第三者に値の参照・書き換えなどをさせないようにできる
  3. 超高速(できれば)…モジュール内にWeakMapを置く方法を取っていたところ、WeakMap#getで律速してしまっていました。これより速い処理が可能であれば行いたいところです。

なお、実行環境としては各ブラウザ(Chrome、Safari、Firefox、Edge)の最新版を前提とします。

自分で試した・調査したこと

以下の手法について調査・あるいは実装してみました。

ES7のプライベートプロパティ(#prop

言語純正の機能でありアクセス権は完璧、オーバーヘッドも薄そうですが、classでしか使えない以上、今回の条件では使えないです。

WeakMap

WeakMapのインスタンスをモジュールローカルにしてしまえばモジュール外からのアクセスは不可能となるのですが、WeakMap#getで書いたコードが、通常のプロパティ参照を行うコードの5分の1ぐらいしか速度が出ず、なんとかならないかと思ったのが今回の質問に至った動機です。

普通のプロパティに保管する

速度的には最速なのですが、もちろん外部からも参照できてしまいます。

シンボルプロパティ

モジュール独自のシンボルを作成して、それをキーにしたプロパティに格納すれば、不意に衝突する可能性もありませんし、Object.keysなどにも登場しません。ただし、Object.getOwnPropertySymbolsでアクセスできるので、完璧ではありません。

クロージャ

クロージャで封じきる、という手段も考えたのですが、当該プロパティを使うメソッドが数十個あるような状況で、メソッドをコピーするコストも半端なさそうなので、あまり現実的な選択肢ではありません。

0

3Answer

class が使えない状況の詳細がわからないので可能かわかりませんが書いてみます。

プライベートな値は class にまとめES7の private フィールド(プロパティ)とし、それらに対するアクセスには public メソッドを通すようにする。
そのクラスのインスタンスを、private プロパティを保持したいオブジェクト(classに出来ないと言われているやつ。Object.create で作ったオブジェクト?)に持たせる。

つまり obj.privateValueStrage.#someValue のような2段階の持ち方にする(class に出来ない問題回避のため)

private フィールドにアクセスするためのメソッドには特定のシンボルまたはシングルトンオブジェクトの参照を引数で渡す必要があるようにして、このシンボルないしオブジェクトをモジュール単位なりクロージャーにする。

結果、外部からは obj.privateValueStrage.getSomeValue(...) のようにアクセスしようとしても、...の箇所に正しいシンボルないしオブジェクトを渡せないのでアクセスできない、と出来そうです(アクセスできる側からしたら手間がかかりますが)。

ちょっと実現性を試せていないので、絵に描いた餅になってたら申し訳ありません。

1Like

Comments

  1. @jkr_2255

    Questioner

    たしかに、両立ができていますね。ここまで厳格に行うかは別問題ですが、1つの提案としてありがとうございました。

@jkr_2255 さん

プライベートな値とする(できれば)…インスタンス、クロージャ、モジュールなど何かしらの単位で値を封じきって、第三者に値の参照・書き換えなどをさせないようにできる

インスタンス単位でプライベート値を生成する事はclass構文の#propで実装できますが、

'use strict';
class Foo {
  #privateValue = 0
  constructor(...args) {
    if (args.length) this.#privateValue = args[0];
  }
  getValue() {
    return this.#privateValue;
  }
}

const foo1 = new Foo(1), foo2 = new Foo(2);
console.log(foo1.getValue()); // 1
console.log(foo2.getValue()); // 2

ES7のプライベートプロパティ(#prop)

言語純正の機能でありアクセス権は完璧、オーバーヘッドも薄そうですが、classでしか使えない以上、今回の条件では使えないです。

「classでしか使えない」が引っかかっています。
どの要件に適合していないのでしょうか。

「インスタンス、クロージャ、モジュールなど何かしらの単位で値を封じきって」の「単位」に何らかの制約がある気がします。

0Like

Comments

  1. @jkr_2255

    Questioner

    > どの要件に適合していないのでしょうか。

    質問本文に書いたように、「元のコードが`new`以外で生成したオブジェクトのプロトタイプを差し替えるなど黒魔術満載のもの」という中、これに合わせざるをえないため、`class`構文を使うこと自体が困難な状況です。
  2. > 質問本文に書いたように、「元のコードが`new`以外で生成したオブジェクトのプロトタイプを差し替えるなど黒魔術満載のもの」という中、これに合わせざるをえないため、`class`構文を使うこと自体が困難な状況です。

    その黒魔術がブラックボックスである以上、解決策の提案が難しいように感じます。

Your answer might help someone💌