JavaScript | WeakMap / WeakSet の動作(GCで自動削除)を視覚化

javascrpit JavaScript
スポンサーリンク

ここでは、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の対象になります。

まとめ

比較項目MapWeakMap
キー型どんな値でもOKオブジェクトのみ
自動削除なしGCで自動削除
size / clearありなし(非列挙)
用途永続データ一時キャッシュ、DOM関連データ

このデモは、
🌿「DOM要素と一緒に一時データを保持したいとき」や
🧠「SPAで不要データがリークするのを防ぎたいとき」の理解に最適です。

タイトルとURLをコピーしました