【実務・中級編】 WeakMapおよびWeakSetの型判定 – JavaScript実践ガイド

WeakMapとWeakSetの「正体」を暴く:メモリリークを防ぐための型判定術

現場でコードを書いていて、「これってWeakMapだっけ?普通のMapだっけ?」と混乱したことはないだろうか。

フロントエンドのパフォーマンスチューニングにおいて、`WeakMap`と`WeakSet`はメモリ管理の切り札だ。しかし、これらは「特殊なオブジェクト」であるがゆえに、安易な判定手法では足元をすくわれることがある。今日は、JavaScriptの深淵を覗きつつ、現場で確実に使える型判定の作法を伝授しよう。

—

なぜWeakMap/WeakSetの判定は難しいのか

まず前提として、`typeof`演算子に頼るのはやめよう。`typeof`はこれらを単なる`”object”`としてしか認識しない。これでは「それがMapなのかWeakMapなのか」を区別できない。

また、`instanceof`を使えば一見解決するように思えるが、実は落とし穴がある。iframeを跨いだコンテキストや、複数の実行環境(Realm)が混在する複雑なアプリケーションでは、プロトタイプチェーンが分断され、`instanceof`が`false`を返すケースがあるのだ。

では、プロはどう判断しているのか?

結論:Object.prototype.toString.call を信じろ

JavaScriptの標準仕様において、組み込みオブジェクトの型を最も堅牢に判定する方法は、`Object.prototype.toString`を直接呼び出すことだ。いわゆる「[object Type]」という文字列を得る手法である。

これを使うと、エンジン内部の `[[Class]]` 内部スロットを直接覗きに行くことができるため、コンテキストの境界を越えて正確に判定できる。

実践的な判定関数のコード

以下は、現場でそのままコピペして使える、堅牢な判定ロジックだ。

/

  • 高精度な型判定ユーティリティ
  • WeakMapやWeakSetを正確に判別します

/
const isWeakMap = (value) => {
// toString.callを使い、[object WeakMap]という文字列を確認する
return Object.prototype.toString.call(value) === ‘[object WeakMap]’;
};

const isWeakSet = (value) => {
// 同様に[object WeakSet]を確認
return Object.prototype.toString.call(value) === ‘[object WeakSet]’;
};

// — テストコード —
const wm = new WeakMap();
const ws = new WeakSet();
const map = new Map();

console.log(isWeakMap(wm)); // true
console.log(isWeakMap(map)); // false
console.log(isWeakSet(ws)); // true

// ちなみに、typeofはどちらも “object” を返してしまい役に立たない
console.log(typeof wm); // “object”

—

ブラウザの裏側で起きていること

なぜ`WeakMap`は特殊なのか。それは、「キーとなるオブジェクトが参照されなくなったら、そのエントリごとガベージコレクション(GC)対象にする」という特殊な契約がエンジンレベルで結ばれているからだ。

そのため、普通の`Map`と違い、`WeakMap`は「列挙不可能(Non-enumerable)」だ。`for…of`で回すことも、`.size`プロパティを確認することもできない。これは仕様上、いつGCが走るか予測できないため、サイズを確定させることが物理的に不可能だからだ。

もしあなたがデバッグ中に `console.log` で中身が見えないと焦ったとしても、それは「ブラウザが壊れている」のではなく、「それがWeakMapの正常な振る舞い」なのだと理解してほしい。

実務での「守り」としての考え方

現場のコードでこれらの判定を行うシーンは、主に以下の2つだろう。

1. ライブラリやユーティリティの引数チェック: 外部から渡されたデータが「キャッシュ用オブジェクト(WeakMap)」なのか「通常のデータ」なのかを判別し、適切な処理に振り分けたいとき。
2. デバッグ・ロギング: メモリリーク調査の際、ヒープスナップショットではなく、実行中のオブジェクトが何の型かをログ出力してトレースしたいとき。

正直に言おう。頻繁に`isWeakMap`を呼び出すような設計になっているなら、それは設計を見直すサインかもしれない。`WeakMap`は「外から隠蔽して保持するもの」であり、その性質上、外部から判定する必要性があまりないはずだからだ。

最後に:シニアからのアドバイス

「型判定」は強力なツールだが、多用しすぎるとコードの柔軟性を損なう。「型を判定して分岐する」のではなく、「ポリモーフィズムで解決できないか?」を常に自問自答してほしい。

それでも、JavaScriptというカオスな言語で開発を続ける以上、今回紹介した`toString.call`という「最後の砦」だけは、必ず記憶の引き出しの奥深くに入れておいてくれ。いざという時、君のコードをバグから救ってくれるはずだ。

次は、もし君が余裕があれば、「Proxyを用いたWeakMapのハック的な拡張」に挑戦してみると面白い。JavaScriptの深淵はまだまだ深いぞ。精進してくれ。

コメント

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