WeakMap概要
Mapと違って、キーとして使われるオブジェクトが生きているうちに、WeakMapがキーの参照を持ちますが、WeakMap以外誰にも使われていなければ、ガベージコレクションによってキーオブジェクトのメモリを解放してしまいます。つまり、キーの参照が弱い参照であり、自動的に削除される可能性があります。
次は、利用ケースでなぜWeakMapが必要なのかを理解しましょう。
利用ケース
下記execute関数が複数回に呼ばれることがあり、同じ引数objで実行される回数が10の倍数になったら、そのobj情報をシステム上に通知を行います。
// マップキーが文字列ではなく、オブジェクトです
let map = new Map();
function execute(obj){
doSomethingWith(obj);
// 既存の回数を取得
const called = (map.get(obj) || 0) + 1;
map.set(obj, called);
// 10回目で
if(called % 10 === 0) {
report(obj);
}
}
- 上記実装の問題点
例え、上記objがシングルページアプリケーションのDOMエレメントの場合、DOMが大量に生成されたり、破棄されたりとします。破棄されたDOMエレメントが上記回数を統計するmapのキーになっているため、GC処理対象外になり、メモリリークが発生してしまいます。
本来は、生きているDOMエレメントを絞って統計したいにもかかわらず、死んでしまったDOMエレメントの情報も残されてしまいます。
- 考えられるアプローチ:DOMエレメントのdeleteイベントでmapから削除する
DOMエレメントのdeleteイベントを監視し、発生したらmapから該当objを削除するように実装すれば、メモリリーク問題が解決になります。
ただ、そうしますと、監視マップとDOMエレメントが相互的に相手の存在を意識しなければならないし、処理も複雑になり、バグが入りやすいですね。
いよいよWeakMapの登場です。
- WeakMap案
GCによりキーオブジェクトの廃棄と共に、WeakMapから該当データが自動的に削除されるので、実装がシンプルになります。
// マップキーが文字列ではなく、オブジェクトです
let map = new WeakMap();
function execute(obj){
doSomethingWith(obj);
// 既存の回数を取得
const called = (map.get(obj) || 0) + 1;
map.set(obj, called); // 設定したが、GCにより自動的に削除される
// 10回目で
if(called % 10 === 0) {
report(obj);
}
}