MENU

JavaScriptのSymbol.hasInstanceとは?instanceofを自在に操る魔法の仕組み

目次

はじめに

JavaScriptにおける型判定は、しばしばinstanceof演算子に頼る場面があります。たとえば、あるオブジェクトが特定のクラスのインスタンスかどうかを調べたいとき、obj instanceof MyClassという構文が使われます。しかし、この判定ロジックは、実はカスタマイズ可能であることをご存知でしょうか?その鍵となるのが、ウェルノウンシンボルのひとつであるSymbol.hasInstanceです。

イティセル/コード専門官

要するに、本来はinstanceofで偽になるはずのオブジェクトを、Symbol.hasInstanceを使って任意のルールで「真」に変えることができるのです。

Symbol.hasInstanceの基本構造

Symbol.hasInstanceは、instanceof演算子が呼び出されたときに、右辺のコンストラクタ関数に定義されていれば実行される静的メソッドの識別子です。通常、instanceofはプロトタイプチェーンを辿って判定を行いますが、Symbol.hasInstanceを使えばその挙動を任意のロジックに置き換えることが可能になります。

イティセル/コード専門官

言い換えると、本来ならinstanceofでfalseになるようなオブジェクトでも、Symbol.hasInstanceを使えばtrueと判定できるようにできる、ということです。

イティセル/コード専門官

Symbol.hasInstanceをもう少し具体的に理解してみましょう。次のサンプルコードを見てください。

class CustomChecker {
  static [Symbol.hasInstance](obj) {
    return obj !== null && obj.isCustom === true;
  }
}
const sample = { isCustom: true };
console.log(sample instanceof CustomChecker);
リクナ / JavaScript統括官

true

実行結果
true
イティセル/コード専門官

CustomCheckerというクラスは、特別な静的メソッドである[Symbol.hasInstance]を定義しています。
このメソッドは、obj instanceof CustomCheckerという式が評価されるときに呼び出される仕組みになっています。  
メソッドの中では「対象のオブジェクトがnullではなく、さらにisCustomプロパティがtrueであればtrueを返す」という判定ロジックが記述されています。つまり、このクラスは「isCustomプロパティがtrueであるオブジェクトを、自分自身のインスタンスとして扱う」という独自のルールを持っているのです。

要するに、通常ならinstanceofでfalseになるオブジェクトでも、Symbol.hasInstanceを使えばtrueと判定できるようにできる、ということです。  
つまり、今のままではinstanceofでfalseになるけれど、Symbol.hasInstanceを定義すればtrueにできる、というわけです。

class CustomChecker {
  static [Symbol.hasInstance](obj) {
    return obj !== null && obj.isCustom === true;
  }
}
イティセル/コード専門官

sampleはnullではなく、さらにsample.isCustomがtrueであるため、この式は最終的にtrueを返します。

return obj !== null && obj.isCustom === true;
イティセル/コード専門官

sampleはただのプレーンなオブジェクトであり、CustomCheckerを継承しているわけでも、new CustomChecker()によって生成されたものでもありません。
したがって、通常の考え方であれば sample instanceof CustomChecker の結果は false になるはずです。

要するに、sampleは単なるリテラルオブジェクト({})なので、そのままではCustomCheckerとの関係はなく、instanceofの判定結果はfalseになります。

const sample = { isCustom: true };
イティセル/コード専門官

instanceof が実行されると、JavaScriptはまず右辺の CustomChecker に Symbol.hasInstance が定義されているかどうかを確認します。定義されていれば、そのメソッドが呼び出され、引数として左辺の sample が渡されます。

要するに、CustomChecker の中にある Symbol.hasInstance が定義されていれば、そのロジックに従って判定が行われ、今回のコードでは結果が true になる、ということです。

console.log(sample instanceof CustomChecker);
リクナ / JavaScript統括官

true

実行結果
true

このように、instanceofの判定基準をプロパティベースに変更することもできるのです。

なぜSymbol.hasInstanceが重要なのか?

この仕組みは、ライブラリ設計やAPI設計において、柔軟な型判定を実現するために非常に有用です。たとえば、特定のインターフェースを満たすオブジェクトを「インスタンス」とみなしたい場合、通常のクラス継承では対応できません。しかし、Symbol.hasInstanceを使えば、構造的型判定やタグ付きオブジェクトの識別など、より高度な判定が可能になります。

イティセル/コード専門官

イメージとしては「チーム分け」に近いです。同じオブジェクトであっても、Symbol.hasInstance を使えば「お前はこっちのチームに所属」とルールを決めて仕分けできる。つまり、通常の instanceof ではできない柔軟な仲間判定を、自分のルールで実現できるのです。
例えば、

1. 入場チェック(ライブやイベント)

•  普通の instanceof → 「身分証を見て、家系(クラス継承)で判断」

•  Symbol.hasInstance → 「合言葉やリストバンドを持っていれば入場OK」  

→ 血筋じゃなくても、条件を満たせば仲間扱いできる。  

2. クラブ活動の部員判定

•  普通の instanceof → 「正式に入部届を出した人だけ部員」

•  Symbol.hasInstance → 「毎日練習に来てるなら部員扱い」  

→ 書類上の所属じゃなく、行動や特徴で判定できる。  

3. ゲームのパーティ編成

•  普通の instanceof → 「戦士クラスを選んだ人だけ戦士パーティ」

•  Symbol.hasInstance → 「剣を持っていれば戦士パーティに入れてあげる」  

→ クラスに縛られず、条件を満たせば仲間にできる。  

4. スーパーのレジ袋判定

•  普通の instanceof → 「公式のレジ袋だけ袋扱い」

•  Symbol.hasInstance → 「袋っぽい形をしていれば袋扱い」  

→ 見た目や特徴で判断できる。  

実践的な応用例

たとえば、配列かどうかを判定するクラスを作ることもできます。

イティセル/コード専門官

ここで、Symbol.hasInstance の仕組みをより深く理解するために、次のサンプルコードを確認してみましょう。

class IsArray {
  static [Symbol.hasInstance](obj) {
    return Array.isArray(obj);
  }
}
console.log([] instanceof IsArray);
IsArray[Symbol.hasInstance]([]);
リクナ / JavaScript統括官

true

実行結果
true
イティセル/コード専門官

IsArray というクラスを定義し、その中に静的メソッド Symbol.hasInstance を実装しています。
instanceof が使われると、このメソッドが呼び出され、左辺のオブジェクトが obj として渡されます。そしてメソッドの中では Array.isArray(obj) を返すようになっているため、渡されたオブジェクトが配列であれば true、そうでなければ false という判定ルールが適用されます。

要するに、IsArray は「渡されたオブジェクトが配列かどうか」を判定して、その結果を返してくれるクラスです。

class IsArray {
  static [Symbol.hasInstance](obj) {
    return Array.isArray(obj);
  }
}
イティセル/コード専門官

左辺には [](空配列)、右辺には IsArray クラスが置かれています。instanceof が実行されると、JavaScript はまず右辺の IsArray に Symbol.hasInstance が定義されているかを確認します。そして定義が見つかると、内部的に IsArray[Symbol.hasInstance]([]) が呼び出される仕組みになっています。

要するに、Symbol.hasInstance が定義されていれば、IsArray クラスが「渡されたオブジェクトは配列か?」と確認し、その判定ルール(Array.isArray(obj))に従って結果を返します。
今回の例では true となるため、[] instanceof IsArray の実行結果は true になります。

console.log([] instanceof IsArray);
イティセル/コード専門官

この呼び出しの中で Array.isArray([]) が実行され、その結果は true となります。
したがって Symbol.hasInstance も true を返し、最終的に [] instanceof IsArray の評価結果は true になります。

要するに、instanceof は自分で直接判定しているのではなく、Symbol.hasInstance に「このオブジェクトは仲間か?」と問い合わせて、その答えを受け取っているようなものです。
電話で結果を教えてもらうイメージに近いですね。

IsArray[Symbol.hasInstance]([]);
リクナ / JavaScript統括官

true

実行結果
true

また、特定のフラグを持つオブジェクトを「インスタンス」とみなすことも可能です。

イティセル/コード専門官

ここで、Symbol.hasInstance の仕組みをより深く理解するために、次のサンプルコードを確認してみましょう。

class Flagged {
  static [Symbol.hasInstance](obj) {
    return obj && obj.flag === true;
  }
}
console.log({ flag: true } instanceof Flagged);

Flagged[Symbol.hasInstance]({ flag: true });
リクナ / JavaScript統括官

true

実行結果
true
イティセル/コード専門官

Flagged というクラスを定義し、その中に静的メソッド Symbol.hasInstance を実装しています。instanceof が使われると、このメソッドが呼び出され、左辺のオブジェクトが obj として渡されます。そしてメソッドの中では obj.flag === true が判定されるため、「flag プロパティが true であれば仲間扱いする」というルールが適用されます。

要するに、どんなオブジェクトが渡されても、そのオブジェクトの flag プロパティが true なら「仲間」とみなす、という仕組みです。

class Flagged {
  static [Symbol.hasInstance](obj) {
    return obj && obj.flag === true;
  }
}
イティセル/コード専門官

左辺には { flag: true } というオブジェクトがあり、右辺には Flagged クラスが置かれています。instanceof が実行されると、JavaScript はまず右辺の Flagged に Symbol.hasInstance が定義されているかどうかを確認します。

要するに、Flagged クラスの中に Symbol.hasInstance が定義されているかを確認します。もし定義されていれば、{ flag: true } というオブジェクトを渡して判定を行い、その結果を返してくれます。

console.log({ flag: true } instanceof Flagged);
イティセル/コード専門官

実際にはこのように呼び出され、引数 obj には { flag: true } が渡されます。そしてメソッドの中で obj.flag === true が評価され、その結果は true となります。

要するに、Flagged が「このオブジェクトは仲間か?」と obj.flag === true に尋ねてみたら「はい、仲間です」と答えが返ってきて、その結果を電話で報告してくれるようなイメージです。

Flagged[Symbol.hasInstance]({ flag: true });
リクナ / JavaScript統括官

true

実行結果
true

このように、Symbol.hasInstanceはクラスの枠を超えた柔軟な判定ロジックを提供してくれます。

注意すべきポイント

ただし、いくつかの注意点もあります。まず、Symbol.hasInstanceは静的メソッドであり、インスタンスメソッドとして定義しても機能しません。また、instanceofの右辺には関数オブジェクトが必要であり、通常のオブジェクトを指定するとTypeErrorが発生します。

さらに、Function.prototype[Symbol.hasInstance]はデフォルトの判定ロジックを提供していますが、これは書き換え不可であり、直接変更することはできません。したがって、カスタマイズしたい場合は、各クラスごとにstatic [Symbol.hasInstance]を定義する必要があります。

イティセル/コード専門官

要するに、Symbol.hasInstance は静的メソッドなら対応しますが、インスタンスメソッドには対応しません。つまり「電話で問い合わせる」ような仕組みです。
もう一つは、共通のデフォルト(Function.prototype)は改造できないので、もし判定ロジックを変えたいなら、独自のクラスを定義してその中にルールを持たせる必要があります。
つまり「同じ仕組みを直接いじる」のではなく、「自分のクラスに独自の判定ルールを与える」ということです。

Symbol.hasInstanceと他のウェルノウンシンボルとの関係

JavaScriptには他にも多くのウェルノウンシンボルが存在します。たとえば、Symbol.iteratorはイテレーションの挙動を定義し、Symbol.toPrimitiveはプリミティブ型への変換を制御します。これらと同様に、Symbol.hasInstanceは言語の根幹に関わる演算子の挙動をカスタマイズできるという点で非常に強力です。

イティセル/コード専門官

ITTI CODEのブログでは、こうしたウェルノウンシンボルについてもわかりやすく解説しています。もし興味があれば、ぜひ読んでみてください。

コラム:実務でのインデックス活用を会話で学ぼう!

ITTI

以上がSymbol.hasInstanceの説明でした。どう思いますか?

AIひろゆき

いや、優秀な人がみんな Symbol.hasInstance を使うわけじゃないですよ。だって、普通に instanceof で十分なケースがほとんどですから。むしろ、こういう仕組みを知ってて“ここぞ”という場面で使える人が優秀なんじゃないですか?
知らない人は一生 instanceof のデフォルト判定に縛られてるわけで、それって柔軟性がないですよね。

ITTI

Symbol.hasInstanceの仕組みについてを覚えることで、他の人が作業しているところを見てすぐに気づいたりスムーズに進めれるようになりますよね。

AIひろゆき

まあ、そうですね。知ってると“あ、ここ独自の判定してるな”ってすぐ分かるんで、余計な手戻りも減りますし。結局、知識がある方が楽に進められるんで、覚えておいて損はないと思いますよ。

まとめ

Symbol.hasInstance は、JavaScript における型判定の枠を広げるための強力なツールです。従来のプロトタイプチェーンに基づく判定に加えて、構造的な条件や特定のプロパティを基準にした判定を可能にすることで、より柔軟で直感的な API 設計やライブラリ構築を実現できます。

たとえば、「特定のフラグを持つオブジェクトを仲間扱いする」、「配列かどうかを独自のクラスで判定する」といった、通常の instanceof では表現できないルールを組み込むことができます。これは、フレームワークやユーティリティライブラリの内部実装において、利用者に自然なインターフェースを提供するために非常に有効です。

特に、型安全性やコードの可読性を重視する中級〜上級の JavaScript 開発者にとっては、知っておくべき重要な機能のひとつです。単なる「トリビア的な知識」ではなく、チーム開発でのレビュー効率向上や柔軟な拡張性を持つライブラリ設計に直結する実践的な知識となります。

要するに、Symbol.hasInstance を理解しておくことは「instanceof を自在に操れるようになる」ことを意味します。今後のコード設計において、この仕組みを活用することで、より洗練された型判定ロジックを実現し、開発体験そのものを一段階引き上げることができるでしょう。

もしこの記事が役に立ったと思ったら、シェアやコメントで教えてください。  いただいた声を今後の改善に活かしていきます。  

最後まで読んでくださり、本当にありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

ITTIのアバター ITTI 運営長

私はフロントエンドエンジニアを目指す初心者で、ITパスポートを取得済みです。現在はCopilotを活用しながらAIや最新のIT技術を学び、日本の開発現場で求められるチーム開発やセキュリティの知識を吸収しています。学んだことはコードや仕組みを整理し、わかりやすく発信することで、同じ学びの途中にいる人たちの力になりたいと考えています。

コメント

コメントする

目次