WeakMap とは何か(まずイメージから)
WeakMap は、
「キーをオブジェクトに限定した、”弱い紐づけ” をするための特別な Map」 です。
普通の Map と似ていますが、決定的に違うのはここです。
Map:キーに何でも使えるし、キーが残っている限り値も残るWeakMap:キーは「オブジェクトだけ」OK。そのオブジェクトが他で使われなくなったら、自動的にこのエントリも消える(ガベージコレクション)
つまり WeakMap は、
「あるオブジェクトに、後から “こっそり追加情報” をくっつけたいけど、
そのオブジェクトが不要になったら、追加情報も一緒に自動で消えてほしい」
というときに使う仕組みです。
ここが重要です。
WeakMap は「メモリリークを防ぎながら、オブジェクトに付随情報を持たせる」のが本来の目的 で、
ふつうの「辞書」として使うには向いていません。
普通の Map と WeakMap の違い(ここをしっかり)
違い1:キーにできるのは「オブジェクトだけ」
const wm = new WeakMap();
const obj = { id: 1 };
wm.set(obj, "何かのデータ"); // OK
wm.set(123, "数値キー"); // エラー:WeakMap のキーはオブジェクトだけ
wm.set("name", "Alice"); // エラー:同上
JavaScriptWeakMap のキーは、必ず「オブジェクト(配列や関数も含む)」でなければなりません。
プリミティブ値(数値・文字列・boolean など)はキーにできません。
Map はどんな値もキーにできるのに対して、WeakMap はかなり制限されています。
違い2:「キーのオブジェクト」が他で使われなくなったら自動的に消える
これが一番重要なポイントです。
let obj = { id: 1 };
const wm = new WeakMap();
wm.set(obj, "追加情報");
// どこかのタイミングで
obj = null; // obj への参照を切る(他に参照がなければ GC 対象)
// しばらくすると、Garbage Collector が働いて
// 元の { id: 1 } オブジェクトがメモリから回収される
// それに紐づいて、WeakMap の中のそのエントリも自動的に消える
JavaScriptここで重要なのは:
Mapの場合:キーとして使っている限り、そのオブジェクトは残り続ける(メモリリークの原因になりうる)WeakMapの場合:他のどこからも参照されなくなったオブジェクトは、WeakMap に入っていても回収対象になる
WeakMap は、「オブジェクトの寿命に追随する付属情報」を持たせるための仕組みと考えると理解しやすいです。
違い3:イテレーションできない(for…of できない)
Map と違って、WeakMap は「中身を全部なめる」ことができません。
const wm = new WeakMap();
const obj1 = {};
const obj2 = {};
wm.set(obj1, 1);
wm.set(obj2, 2);
// for (const [k, v] of wm) {} // これはエラー
// wm.keys() や wm.values() もない
JavaScriptfor...of、forEach、keys などがありません。
なぜかというと、
- GC(ガベージコレクション)のタイミングは自由
- いつの間にかキーが消えているかもしれない
- そういう「消えるかもしれないもの」の一覧を常に提供するのは矛盾する
という設計上の理由があります。
ここが重要です。
WeakMap は「こっそり付随情報を持たせる」ためのものであって、「全体を一覧したりサイズを数えたりする用途には向いていない」 のです。
違い4:size プロパティがない
Map には size がありましたが、WeakMap にはありません。
const wm = new WeakMap();
// wm.size; // そんなものはない(エラー)
JavaScriptこれも「いつ GC によって減るか分からない」性質から来ています。
WeakMap の中身は「数えない・見ない」「必要なキーを知っている前提で、ピンポイントでアクセスする」使い方になります。
WeakMap の基本操作(set / get / has / delete)
new WeakMap() で作る
const wm = new WeakMap();
JavaScriptset で追加(キーはオブジェクトのみ)
const wm = new WeakMap();
const user = { id: 1, name: "Alice" };
wm.set(user, { lastLogin: "2025-01-01" }); // OK
JavaScriptget / has / delete も Map と同じ形
console.log(wm.get(user)); // { lastLogin: "2025-01-01" }
console.log(wm.has(user)); // true
wm.delete(user); // 削除
console.log(wm.has(user)); // false
JavaScriptAPI の形は Map とよく似ていますが、「キーがオブジェクト限定」「サイズが取れない」「ループできない」という大きな制約があります。
WeakMap が活きる場面(ここが本質)
パターン1:オブジェクトに「外から」付随データをつけたい
例えば、「DOM 要素ごとに追加情報を持たせたい」が、
DOM 自体にはプロパティを増やしたくないし、
DOM が削除されたら追加情報も自動で消えてほしい、という場合。
// DOM 要素ごとに追加情報をメモしておく WeakMap
const elementData = new WeakMap();
function setElementData(el, data) {
elementData.set(el, data);
}
function getElementData(el) {
return elementData.get(el);
}
JavaScript使い方イメージ:
const div = document.createElement("div");
setElementData(div, { clicked: 0 });
div.addEventListener("click", () => {
const info = getElementData(div);
info.clicked++;
console.log("クリック回数:", info.clicked);
});
// 後で DOM を破棄する
document.body.removeChild(div);
// 他に div を参照していなければ、
// div とそれに紐づく WeakMap のデータは GC で回収される
JavaScriptここが重要です。
WeakMap は「元のオブジェクトに直接プロパティを足したくないが、関連情報を持ちたい」場面で使える。
しかもメモリリークを起こしにくい形で。
パターン2:クラスインスタンスの「内部状態」を隠す
プライベートフィールド # が登場する前は、
WeakMap を使って「本物のプライベート状態」を実現するパターンがよくありました。
const _private = new WeakMap();
class User {
constructor(name) {
const privateData = { nameUpper: name.toUpperCase() };
_private.set(this, privateData);
this.name = name; // 公開プロパティ
}
get upperName() {
return _private.get(this).nameUpper;
}
}
const u = new User("Alice");
console.log(u.upperName); // ALICE
// _private の中身には外からアクセスできない
// u._private などもない
JavaScript今はクラスの #フィールド を使うほうが素直ですが、
クラス外で「特定のインスタンスにだけ紐づく隠し情報」を持ちたいときには、
WeakMap が今も有効です。
パターン3:キャッシュ(メモ化)でメモリリークを防ぎたい
関数の結果をキャッシュして再利用する「メモ化」というテクニックがあります。
例えば、オブジェクトを元に重い計算をするとき:
const cache = new WeakMap();
function heavyCalc(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
// 実際はもっと重い処理…
const result = JSON.stringify(obj);
cache.set(obj, result);
return result;
}
JavaScriptこの場合、obj がどこからも参照されなくなれば、cache 内のエントリも GC により消えます。
普通の Map でキャッシュすると、
「Map がずっと obj を参照し続ける → obj が永遠に生き続ける → メモリリーク」
となりかねませんが、WeakMap ならそれを避けられます。
ここが重要です。
「オブジェクト → 計算結果」のキャッシュを長期的に保持したいけれど、
使われなくなったオブジェクトに紐づくキャッシュは捨ててよい」
という状況で WeakMap はとても役立つ です。
初心者が意識しておきたい「注意点」
単なる「キーがオブジェクトの Map」としては使わないほうがよい
「キーがオブジェクトの Map が欲しいんだよね」と思って WeakMap を選ぶと、
だいたい痛い目を見ます。
理由は:
- ループできない(中身を一覧できない)
- サイズが分からない
- いつエントリが消えるか分からない
という制限のせいで、「ふつうの辞書」としてはとても使いにくいからです。
「キーがオブジェクトの辞書」が欲しいなら、
まずは 普通の Map を選ぶのが正解です。
WeakMap は、「ガベージコレクションとメモリリーク」に意識が向いてきた頃に、
「オブジェクトにぶらさげる“弱い紐づけ”」として使うものだと思ってください。
「いつ消えるか」はプログラマーからは見えない
WeakMap のエントリが消えるタイミングは、
JavaScript エンジンのガベージコレクタに完全に委ねられています。
- すぐに消えるかもしれない
- しばらく残っているかもしれない
これは仕様として保証されていませんし、されてはいけません。
そのため、
- 「何個入っているか」でロジックを組まない
- 「全部を列挙して保存し直す」といったことを考えない
ことが大事です。
ここが重要です。
WeakMap を使うときは、「中身を数えない・覗かない・列挙しない」という前提を受け入れる必要がある。
あくまで「既知のオブジェクトをキーとして、追加情報を紐づける」用途に限定するのが安全です。
まとめ
WeakMap の核心は、
「キーにオブジェクトだけを使い、そのオブジェクトが不要になったら、自動的に WeakMap 側のデータも捨ててもらえる、メモリに優しい Map」
という点です。
押さえておきたいポイントは:
- キーはオブジェクトだけ(数値や文字列はキーにできない)
- そのキーオブジェクトが他で参照されなくなれば、WeakMap 内のエントリも GC により自動で消える
- ループ(for…of)、keys/values/entries、size などは一切使えない
- API は
set / get / has / deleteが中心で、既知のオブジェクトに対してピンポイントに使う - DOM 要素やクラスインスタンスなどに「外からこっそり付随情報を持たせたいとき」、キャッシュのメモリリークを避けたいときに特に役立つ
- 「キーがオブジェクトの辞書が欲しい」だけなら WeakMap ではなく普通の Map を使う
初心者のうちは、まず Map と Set をしっかり使いこなせるようになってから、
「オブジェクトに追加情報を付けたいけど、メモリリークさせたくない」というシチュエーションが見えてきたタイミングで、
WeakMap を「そういえばあったな」と思い出してもらえれば十分です。
そのときは、「Strong(普通の)Map」と「WeakMap」がメモリと寿命に対してどう違うかを意識しながら、
実際に小さなサンプルを書いて挙動を確かめてみてください。
