1. ホーム
  2. ジャバスクリプト

[解決済み】ES6 WeakMapの実際の使い道は?

2022-03-23 21:03:04

質問

の実際の使い方はどのようなものですか? WeakMap データ構造は ECMAScript 6 で導入されたものですか?

弱いマップのキーは対応する値への強い参照を作成するので、弱いマップに挿入された値が確実に 決して は、そのキーが生きている限り消滅するので、メモ帳やキャッシュなど、通常弱参照や弱値を持つマップなどを使用することはできません。

と思われるのですが、どうでしょう。

weakmap.set(key, value);

...は、遠回しに言っているだけです。

key.value = value;

具体的にどのような使用例があるのでしょうか?

どのように解決するのか?

基本的なこと

WeakMapは、ガベージコレクションに干渉することなく、外部からオブジェクトを拡張する方法を提供します。 あるオブジェクトを拡張したいが、それがシールされているために拡張できない場合、あるいは外部ソースから拡張する場合、いつでも WeakMap を適用することができます。

WeakMapはマップ(辞書)であり、その中に キー へのすべての参照が弱い場合です。 キー が失われ、値への参照もなくなる。 はガベージコレクションされる可能性があります。まず例で示し、少し説明し、最後に実際に使ってみましょう。

あるAPIを使って、あるオブジェクトを手に入れたとしよう。

var obj = getObjectFromLibrary();

さて、このオブジェクトを使用するメソッドができました。

function useObj(obj){
   doSomethingWith(obj);
}

あるオブジェクトで何回メソッドが呼ばれたかを記録し、それがN回以上発生したら報告したい。素直に考えれば、Mapを使えばいいと思う。

var map = new Map(); // maps can have object keys
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

これは動作しますが、メモリリークが発生します。関数に渡されたライブラリオブジェクトの一つ一つを追跡することになり、ライブラリオブジェクトがガベージコレクションされるのを防いでしまうからです。その代わりに WeakMap :

var map = new WeakMap(); // create a weak map
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

そして、メモリリークは解消されました。

使用例

そうでなければメモリーリークが発生するようなユースケースで、以下のようなものが有効です。 WeakMap を含む。

  • 特定のオブジェクトに関するプライベートなデータを保持し、マップへの参照を持つ人々だけにアクセスを許可する。よりアドホックなアプローチはprivate-symbolsの提案でやってきますが、それはずっと先の話です。
  • ライブラリオブジェクトに関するデータを、変更したりオーバーヘッドを発生させることなく保持することができます。
  • 同じ型のオブジェクトが多数存在する場合、JSエンジンが同じ型のオブジェクトに使用する隠しクラスで問題を起こさないように、小さなオブジェクトのセットについてのデータを保持する。
  • ブラウザのDOMノードなど、ホストオブジェクトに関するデータを保持する。
  • 外部からオブジェクトに能力を追加する(他の回答のイベントエミッターの例のように)。

実際の使用例を見てみましょう

外部からオブジェクトを拡張するために使用することができます。Node.jsの実世界から実用的な(脚色された、ある種の現実的な-ポイントを押さえた)例を挙げてみましょう。

例えば、あなたがNode.jsで Promise オブジェクトを作成します。ここで、現在拒否されているすべてのプロミスを追跡したいとします。 ではない への参照が存在しない場合にガベージコレクションされないようにしたい。

さて、あなたは はしない。 ネイティブ・オブジェクトにプロパティを追加することは、明白な理由で、あなたは行き詰っています。もしあなたがプロミスへの参照を保持するならば、ガベージコレクションが起こらないので、メモリリークを引き起こすことになります。もしあなたが参照を保持しないなら、あなたは個々の約束についての追加情報を保存することができません。約束のIDを保存することを含むすべてのスキームは、本質的にあなたがそれへの参照を必要とすることを意味します。

WeakMapsの導入

WeakMapとは キー は弱い。弱いマップを列挙したり、そのすべての値を取得する方法はありません。弱いマップでは、キーに基づいてデータを保存することができ、キーがガベージコレクションされると、値もガベージコレクションされます。

つまり、プロミスがあれば、それに関する状態を保存することができ、そのオブジェクトはガベージコレクションされる可能性があるということです。後でオブジェクトへの参照を得た場合、それに関連する状態があるかどうかをチェックし、それを報告することができます。

を実装するために使用されました。 未処理拒絶フック として、ペトカ・アントノフが これ :

process.on('unhandledRejection', function(reason, p) {
    console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
    // application specific logging, throwing an error, or other logic here
});

プロミスに関する情報をマップに保存し、拒否されたプロミスがいつ処理されたかを知ることができます。