What are the actual uses of ES6 WeakMap? Ask Question

What are the actual uses of ES6 WeakMap? Ask Question

What are the actual uses of the WeakMap data structure introduced in ECMAScript 6?

Since a key of a weak map creates a strong reference to its corresponding value, ensuring that a value which has been inserted into a weak map will never disappear as long as its key is still alive, it can't be used for memo tables, caches or anything else that you would normally use weak references, maps with weak values, etc. for.

It seems to me that this:

weakmap.set(key, value);

...is just a roundabout way of saying this:

key.value = value;

What concrete use cases am I missing?

ベストアンサー1

Fundamentally

WeakMaps provide a way to extend objects from the outside without interfering with garbage collection. Whenever you want to extend an object but can't because it is sealed - or from an external source - a WeakMap can be applied.

A WeakMap is a map (dictionary) where the keys are weak - that is, if all references to the key are lost and there are no more references to the value - the value can be garbage collected. Let's show this first through examples, then explain it a bit and finally finish with real use.

Let's say I'm using an API that gives me a certain object:

var obj = getObjectFromLibrary();

Now, I have a method that uses the object:

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

I want to keep track of how many times the method was called with a certain object and report if it happens more than N times. Naively one would think to use a 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);
}

This works, but it has a memory leak - we now keep track of every single library object passed to the function which keeps the library objects from ever being garbage collected. Instead - we can use a 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);
}

And the memory leak is gone.

Use cases

Some use cases that would otherwise cause a memory leak and are enabled by WeakMaps include:

  • Keeping private data about a specific object and only giving access to it to people with a reference to the Map. A more ad-hoc approach is coming with the private-symbols proposal but that's a long time from now.
  • Keeping data about library objects without changing them or incurring overhead.
  • Keeping data about a small set of objects where many objects of the type exist to not incur problems with hidden classes JS engines use for objects of the same type.
  • Keeping data about host objects like DOM nodes in the browser.
  • Adding a capability to an object from the outside (like the event emitter example in the other answer).

Let's look at a real use

It can be used to extend an object from the outside. Let's give a practical (adapted, sort of real - to make a point) example from the real world of Node.js.

Let's say you're Node.js and you have Promise objects - now you want to keep track of all the currently rejected promises - however, you do not want to keep them from being garbage collected in case no references exist to them.

さて、明らかな理由から、ネイティブ オブジェクトにプロパティを追加したくないので、行き詰まってしまいます。Promise への参照を保持すると、ガベージ コレクションが実行されないため、メモリ リークが発生します。参照を保持しないと、個々の Promise に関する追加情報を保存できません。Promise の ID を保存するスキームは、本質的に、その参照が必要であることを意味します。

WeakMapsの登場

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

これは、Promise が与えられれば、それに関する状態を保存でき、そのオブジェクトは引き続きガベージ コレクションできることを意味します。後で、オブジェクトへの参照を取得した場合、それに関連する状態があるかどうかを確認し、それを報告できます。

これは実装に使用されました未処理の拒否フックペトカ・アントノフこれ:

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
});

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

おすすめ記事