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

javascrpit JavaScript
スポンサーリンク

以下は、WeakMapを使ってイベントリスナーをキャッシュし、メモリリークを防ぐ実践デモのコード例です。ブラウザで動作します。コメント入りで仕組みをわかりやすく説明しています。

目的

DOM要素をキーにしてイベントリスナーを管理する際、
Mapだと要素を削除しても参照が残りガベージコレクションされない可能性があります。
WeakMapを使うと、要素がDOMから削除されると自動で解放されます。

See the Pen WeakMap Event Listener Cache Demo by MONO365 -Color your days- (@monoqlo365) on CodePen.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>WeakMap イベントリスナーキャッシュ デモ</title>
<style>
  body { font-family: sans-serif; padding: 20px; }
  .btn { margin: 5px; padding: 10px 15px; border: 1px solid #888; cursor: pointer; }
  #log { margin-top: 15px; white-space: pre-line; }
</style>
</head>
<body>

<h2>🔗 WeakMap でイベントリスナーをキャッシュ</h2>
<button id="add" class="btn">要素を追加</button>
<button id="remove" class="btn">要素を削除</button>

<div id="container"></div>
<div id="log"></div>

<script>
// --- WeakMap に「DOM要素 → イベントリスナー関数」を保存 ---
const listenerCache = new WeakMap();

const container = document.getElementById('container');
const log = document.getElementById('log');

document.getElementById('add').onclick = () => {
  const btn = document.createElement('button');
  btn.textContent = 'クリックしてみて!';
  btn.className = 'btn';

  // イベントリスナー作成
  const listener = () => {
    log.textContent += '✅ ボタンがクリックされました!\n';
  };

  // WeakMap にキャッシュ
  listenerCache.set(btn, listener);

  // イベント登録
  btn.addEventListener('click', listener);

  container.appendChild(btn);
  log.textContent += '🟢 新しいボタンを追加しました。\n';
};

document.getElementById('remove').onclick = () => {
  if (container.firstChild) {
    const btn = container.firstChild;

    // WeakMap から削除操作は不要!
    // DOMから削除すると参照がなくなり、自動でGC対象になる。
    container.removeChild(btn);
    log.textContent += '🔴 ボタンを削除しました(GCに任せます)\n';
  } else {
    log.textContent += '⚠️ 削除する要素がありません。\n';
  }
};

// ✅ 開発ツールのメモリプロファイラで確認すると、
// WeakMapを使うことでDOM削除後にリスナーも解放されるのが分かります。
</script>

</body>
</html>
HTML

解説

比較項目MapWeakMap
キーの型何でも可オブジェクトのみ(DOM要素含む)
ガベージコレクション手動削除しない限り保持キーが破棄されると自動解放
イテレーション(for…of等)可能不可(内部情報は非公開)
メモリリーク防止❌ 難しい✅ 自動的に解放される

応用例

  • イベントリスナーのキャッシュ
  • DOMノードごとの一時データ(状態やカウントなど)
  • ライブラリ内部の「非公開データ格納」
タイトルとURLをコピーしました