ここでは、WeakMap を使ってメモリリークを防ぐ実践例を、アニメーション&実験付きで理解できるようにします。
目的
JavaScript で DOM 要素やオブジェクトに関連データを紐づけるとき、
Map を使うと参照が残ってメモリリークを起こすことがあります。
WeakMap を使えば、対象がGCされると自動でデータも解放されるという仕組みを体験できます。
See the Pen Practical Demo: Preventing Memory Leaks with WeakMap by MONO365 -Color your days- (@monoqlo365) on CodePen.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WeakMapでメモリリークを防ぐ実践デモ</title>
<style>
body {
font-family: "Segoe UI", sans-serif;
background: #f8fafc;
color: #333;
text-align: center;
padding: 30px;
}
h1 { color: #0078d7; }
.container {
display: flex;
justify-content: center;
gap: 40px;
flex-wrap: wrap;
margin-top: 20px;
}
.panel {
width: 42%;
background: #fff;
border-radius: 12px;
border: 2px solid #0078d7;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
padding: 20px;
}
h2 {
background: #0078d7;
color: white;
border-radius: 6px;
padding: 5px 0;
}
button {
padding: 8px 14px;
margin: 5px;
border: none;
border-radius: 8px;
background: #0078d7;
color: white;
font-weight: bold;
cursor: pointer;
}
button:hover { background: #005fa3; }
.log {
background: #f0f9ff;
text-align: left;
padding: 10px;
border-left: 5px solid #0078d7;
border-radius: 8px;
height: 180px;
overflow-y: auto;
font-size: 0.9em;
margin-top: 10px;
}
.highlight {
background: #fff7c2;
transition: background 0.5s;
}
.dom-item {
background: #eef6ff;
padding: 6px;
border-radius: 6px;
margin: 6px auto;
width: 80%;
}
</style>
</head>
<body>
<h1>🧠 WeakMapでメモリリークを防ぐ実践デモ</h1>
<p>DOM要素やオブジェクトにデータを紐づけるときの安全な方法を体験しよう!</p>
<div class="container">
<div class="panel">
<h2>🚫 Map を使用(リークの可能性あり)</h2>
<div id="mapArea"></div>
<button id="mapAdd">要素を追加</button>
<button id="mapRemove">削除(参照切り)</button>
<div id="mapLog" class="log"></div>
</div>
<div class="panel">
<h2>✅ WeakMap を使用(安全に解放)</h2>
<div id="weakMapArea"></div>
<button id="weakAdd">要素を追加</button>
<button id="weakRemove">削除(参照切り)</button>
<div id="weakLog" class="log"></div>
</div>
</div>
<script>
const map = new Map();
const weakMap = new WeakMap();
const mapArea = document.getElementById("mapArea");
const weakMapArea = document.getElementById("weakMapArea");
const mapLog = document.getElementById("mapLog");
const weakLog = document.getElementById("weakLog");
let mapObj = null;
let weakObj = null;
function log(el, msg) {
el.innerHTML += msg + "<br>";
el.scrollTop = el.scrollHeight;
}
document.getElementById("mapAdd").addEventListener("click", () => {
mapObj = document.createElement("div");
mapObj.textContent = "🧩 Mapの要素";
mapObj.className = "dom-item";
mapArea.appendChild(mapObj);
map.set(mapObj, { data: "追加情報" });
log(mapLog, "✅ Map: 要素を追加 & データを紐づけ");
});
document.getElementById("weakAdd").addEventListener("click", () => {
weakObj = document.createElement("div");
weakObj.textContent = "🍃 WeakMapの要素";
weakObj.className = "dom-item";
weakMapArea.appendChild(weakObj);
weakMap.set(weakObj, { data: "一時的な情報" });
log(weakLog, "✅ WeakMap: 要素を追加 & データを紐づけ");
});
document.getElementById("mapRemove").addEventListener("click", () => {
if (mapObj) {
mapArea.removeChild(mapObj);
mapObj = null; // 参照を切っても...
log(mapLog, "⚠️ DOMは消えたが Map が参照を保持 → GCされない");
log(mapLog, "💀 メモリリークの可能性あり!");
}
});
document.getElementById("weakRemove").addEventListener("click", () => {
if (weakObj) {
weakMapArea.removeChild(weakObj);
weakObj = null;
log(weakLog, "🌿 DOM削除 & 参照も切れた");
log(weakLog, "💨 WeakMap はキーが消えると自動でデータも削除");
}
});
</script>
</body>
</html>
HTML実演シナリオ
| ステップ | 操作 | Map の結果 | WeakMap の結果 |
|---|---|---|---|
| 1 | 要素を追加 | 要素と追加データを保持 | 要素と一時データを紐づけ |
| 2 | 要素削除(参照切断) | DOMは消えるがMap内にデータが残る | GCでデータ自動削除 |
| 3 | メモリ状況 | 参照が残り続ける(リーク) | 安全に開放 |
解説
- Map
- 参照を持ち続けるので、削除してもデータは残ります。
- 長時間動くアプリではメモリリークの原因に。
- WeakMap
- キー(オブジェクト)が他で参照されなくなると、
そのデータは 自動でGCの対象になります。
- キー(オブジェクト)が他で参照されなくなると、
まとめ
| 比較項目 | Map | WeakMap |
|---|---|---|
| キー型 | どんな値でもOK | オブジェクトのみ |
| 自動削除 | なし | GCで自動削除 |
| size / clear | あり | なし(非列挙) |
| 用途 | 永続データ | 一時キャッシュ、DOM関連データ |
このデモは、
🌿「DOM要素と一緒に一時データを保持したいとき」や
🧠「SPAで不要データがリークするのを防ぎたいとき」の理解に最適です。


