MapとSetを「正しく」検知する:深淵なる型判定のアーキテクチャ
フロントエンドの戦場では、`typeof` が使い物にならない瞬間に幾度となく出くわすはずだ。プリミティブ型の判定には便利だが、`Map` や `Set` のようなコレクションオブジェクトに対しては、無慈悲にも `”object”` という無益な文字列を返してくる。
我々のようなアプリケーションアーキテクトにとって、型判定の曖昧さはバグの温床だ。特に、外部ライブラリから渡されたデータが「本当にMapなのか? それともただのプレーンなオブジェクトなのか?」を判別できないまま処理を進めると、後々、メモリリークや意図しないレンダリングの再発火という形でしっぺ返しを食らうことになる。
今回は、JavaScriptの深淵に触れつつ、堅牢なシステムを構築するための「コレクション判定」の極意を伝授しよう。
—
1. なぜ `typeof` は絶望的なのか
まず、現実を見よう。`typeof new Map()` は `”object”` を返す。これでは、`Object.keys()` を叩いて `TypeError` を発生させる未来しか見えない。
const collection = new Map();
console.log(typeof collection); // “object” …何も教えてくれない
// 雑な判定で突き進むと、将来的にこうなる
if (typeof collection === ‘object’) {
// ここで Map に Object.keys を適用しようとして死ぬ
// メモリ負荷の高い巨大な Map を誤って走査しようとすれば、ブラウザのメインスレッドは悲鳴を上げる
Object.keys(collection).forEach(key => { … });
}
我々が求めるのは、型安全かつブラウザエンジンに余計な負荷をかけない判定手法だ。
—
2. `instanceof` の光と影
最も直感的なのは `instanceof` だ。`Map.prototype` がターゲットのプロトタイプチェーンに含まれているかを判定する。しかし、これには「iframeの壁」という有名な罠がある。
// 標準的な判定
if (data instanceof Map) {
// 処理
}
しかし、別のウィンドウ(iframeなど)から渡されたオブジェクトの場合、`instanceof` は `false` を返す。プロトタイプチェーンが別々のグローバルコンテキストで断絶しているからだ。堅牢なライブラリやフレームワークのコア層を書くなら、この挙動を前提にしておく必要がある。
—
3. 至高の判定手法:`Object.prototype.toString`
ブラウザエンジンが内部で保持する `[[Class]]` 情報を直接覗き込むのが、現場で最も信頼されている手法だ。`Symbol.toStringTag` を利用した判定は、競合のリスクも低く、極めて高いパフォーマンスを誇る。
/
- 高速かつ堅牢な型判定ユーティリティ
- プロトタイプチェーンの走査を最小限に抑える
/
const getTag = (value) => Object.prototype.toString.call(value);
function isMap(value) {
return getTag(value) === ‘[object Map]’;
}
function isSet(value) {
return getTag(value) === ‘[object Set]’;
}
// なぜこれが最強なのか?
// 1. 外部コンテキスト(iframe)の影響を受けない
// 2. ブラウザエンジンが保持する内部スロットを直接参照するため高速
// 3. ユーザーが Symbol.toStringTag を汚染しない限り、誤判定のリスクが極めて低い
—
4. アーキテクトとしての考察:パフォーマンスと副作用
「なぜわざわざ関数に切り出すのか?」と思うかもしれない。しかし、高頻度で実行されるデータ変換パイプラインや、状態管理ライブラリの中核において、この判定処理は数千万回単位で呼び出される。
- レンダリング負荷の低減: 誤った型判定で不必要な `Object.entries()` や `Array.from()` を実行すれば、メモリ上に巨大な一時オブジェクトが生成され、ガベージコレクション(GC)の負荷が跳ね上がる。これは特に低スペックなモバイル端末でのUIスタッタリング(カクつき)の主原因となる。
- 非同期の競合回避: 複数の非同期処理が同じデータ構造を共有する場合、型を誤認したまま「非破壊的な更新」を試みると、参照透過性が崩壊する。`Map` や `Set` を厳密に判定し、適切にコピーや変換を行う戦略をとることで、バグを未然に防げる。
結論:プロの矜持
JavaScriptは自由だが、その自由さは「型」という規律によって守らなければならない。`instanceof` で安易に済ませるか、`toString` で内部構造を射抜くか。その選択一つが、数年後に保守するエンジニアの寿命を左右する。
我々アーキテクトが目指すべきは、フレームワークの内部挙動を理解し、どんなに複雑なデータ構造が流れてきても「型」を正しくハンドリングし、メインスレッドを止めない、冷徹なまでの効率性を追求したコードだ。
次のコードレビューで、`typeof` を見かけたらこう言ってやってくれ。「その甘い判定、君のアプリケーションを破壊するよ」と。

コメント