instanceof
演算子を使用すると、オブジェクトが特定のクラスに属しているかどうかを確認できます。継承も考慮されます。
このようなチェックは多くの場合に必要となる場合があります。たとえば、型に応じて引数を異なる方法で処理する多態性関数を構築するために使用できます。
構文は次のとおりです。
obj クラスのインスタンス
obj
そのClass
またはそれを継承するクラスに属している場合はtrue
を返します。
例えば:
クラスウサギ {} let Rabbit = new Rabbit(); // Rabbitクラスのオブジェクトですか? alert( Rabbit インスタンスof Rabbit ); // 真実
コンストラクター関数でも動作します。
// クラスの代わりに 関数 Rabbit() {} alert( new Rabbit() インスタンスの Rabbit ); // 真実
…そしてArray
のような組み込みクラスを使用すると:
arr = [1, 2, 3] とします。 alert( arr 配列のインスタンス ); // 真実 アラート(arr オブジェクトのインスタンス); // 真実
arr
もObject
クラスに属していることに注意してください。これは、 Array
プロトタイプとしてObject
を継承するためです。
通常、 instanceof
チェックのためにプロトタイプ チェーンを調べます。静的メソッドSymbol.hasInstance
にカスタム ロジックを設定することもできます。
obj instanceof Class
のアルゴリズムは、大まかに次のように動作します。
静的メソッドSymbol.hasInstance
がある場合は、それをClass[Symbol.hasInstance](obj)
と呼びます。 true
またはfalse
を返すはずです。これで完了です。これが、 instanceof
の動作をカスタマイズする方法です。
例えば:
// 以下を前提としたinstanceOfチェックを設定します。 // canEat プロパティを持つものはすべて動物です クラス動物{ static [Symbol.hasInstance](obj) { (obj.canEat) が true を返す場合。 } } let obj = { canEat: true }; アラート(動物のオブジェクトインスタンス); // true: Animal[Symbol.hasInstance](obj) が呼び出されます
ほとんどのクラスにはSymbol.hasInstance
がありません。その場合、標準ロジックが使用されます。obj obj instanceOf Class
Class.prototype
obj
プロトタイプ チェーン内のプロトタイプの 1 つと等しいかどうかをチェックします。
言い換えれば、次のように次々と比較します。
obj.__proto__ === クラス.プロトタイプ? obj.__proto__.__proto__ === クラス.プロトタイプ? obj.__proto__.__proto__.__proto__ === Class.prototype? ... // いずれかの答えが true の場合、true を返します // それ以外の場合、チェーンの最後に到達した場合は false を返します
上の例では、 rabbit.__proto__ === Rabbit.prototype
なので、すぐに答えが得られます。
継承の場合、照合は 2 番目のステップで行われます。
クラス動物 {} クラス Rabbit は Animal {} を拡張します let Rabbit = new Rabbit(); アラート(動物のウサギのインスタンス); // 真実 // Rabbit.__proto__ === Animal.prototype (一致しません) // Rabbit.__proto__.__proto__ === Animal.prototype (一致!)
以下は、 rabbit instanceof Animal
とAnimal.prototype
を比較する図です。
ちなみに、 objA.isPrototypeOf(objB) というメソッドもあります。これは、 objA
objB
のプロトタイプのチェーンのどこかにある場合にtrue
返します。したがって、 obj instanceof Class
のテストはClass.prototype.isPrototypeOf(obj)
と言い換えることができます。
面白いことに、 Class
コンストラクター自体はチェックに参加しません。プロトタイプのチェーンとClass.prototype
のみが重要です。
これにより、オブジェクトの作成後にprototype
プロパティが変更されると、興味深い結果が生じる可能性があります。
ここのように:
関数 Rabbit() {} let Rabbit = new Rabbit(); // プロトタイプを変更しました Rabbit.prototype = {}; // ...もうウサギではありません! alert( Rabbit インスタンスof Rabbit ); // 間違い
プレーンオブジェクトが[object Object]
として文字列に変換されることはすでにわかっています。
obj = {} にします。 アラート(obj); // [オブジェクト オブジェクト] アラート(obj.toString()); // 同じ
それがtoString
の実装です。しかし、 toString
実際にはそれよりもはるかに強力にする隠れた機能があります。これを拡張typeof
として、また、 instanceof
の代替として使用できます。
奇妙に聞こえますか?確かに。謎を解いてみましょう。
仕様により、組み込みのtoString
オブジェクトから抽出し、他の値のコンテキストで実行できます。そして、その結果はその値に依存します。
数値の場合は[object Number]
となります。
ブール値の場合、 [object Boolean]
になります。
null
の場合: [object Null]
undefined
の場合: [object Undefined]
配列の場合: [object Array]
…など(カスタマイズ可能)。
実演してみましょう:
// 便宜上、toString メソッドを変数にコピーします objectToString = Object.prototype.toString; にします。 // これは何の型ですか? arr = []; とします。 alert( objectToString.call(arr) ); // [オブジェクト配列]
ここでは、「デコレータと転送」の章で説明されているように call/apply を使用して、 this=arr
コンテキストで関数objectToString
を実行しました。
内部的には、 toString
アルゴリズムがthis
検査し、対応する結果を返します。その他の例:
let s = Object.prototype.toString; アラート( s.call(123) ); // [オブジェクト番号] アラート( s.call(null) ); // [オブジェクト Null] アラート( s.call(アラート) ); // [オブジェクト関数]
オブジェクトtoString
の動作は、特別なオブジェクト プロパティSymbol.toStringTag
を使用してカスタマイズできます。
例えば:
ユーザー = { にします [Symbol.toStringTag]: "ユーザー" }; アラート( {}.toString.call(user) ); // [オブジェクト ユーザー]
ほとんどの環境固有のオブジェクトには、そのようなプロパティがあります。以下にブラウザ固有の例をいくつか示します。
// 環境固有のオブジェクトとクラスの toStringTag: alert( window[Symbol.toStringTag]); // ウィンドウ alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest alert( {}.toString.call(window) ); // [オブジェクトウィンドウ] alert( {}.toString.call(new XMLHttpRequest()) ); // [オブジェクト XMLHttpRequest]
ご覧のとおり、結果は正確にSymbol.toStringTag
(存在する場合) であり、 [object ...]
にラップされています。
最後に、プリミティブ データ型だけでなく、組み込みオブジェクトにも機能し、カスタマイズすることもできる「強化された typeof」があります。
単にチェックするだけではなく文字列として型を取得したい場合は、組み込みオブジェクトのinstanceof
の代わりに{}.toString.call
を使用できます。
私たちが知っている型チェック方法をまとめてみましょう。
のために働く | 返品 | |
---|---|---|
typeof | プリミティブ | 弦 |
{}.toString | プリミティブ、組み込みオブジェクト、 Symbol.toStringTag を持つオブジェクト | 弦 |
instanceof | オブジェクト | 真/偽 |
ご覧のとおり、 {}.toString
技術的には「より高度な」 typeof
です。
そして、 instanceof
演算子は、クラス階層を操作していて、継承を考慮してクラスをチェックしたい場合に非常に役立ちます。
重要度: 5
以下のコードで、 instanceof
true
を返すのはなぜですか? a
B()
によって作成されたものではないことが簡単にわかります。
関数 A() {} 関数 B() {} A.プロトタイプ = B.プロトタイプ = {}; a = 新しい A(); とします。 alert( B のインスタンス); // 真実
ええ、確かに奇妙に見えます。
しかし、 instanceof
関数ではなく、プロトタイプチェーンと照合するそのprototype
を考慮します。
そしてここではa.__proto__ == B.prototype
なので、 instanceof
true
を返します。
そのため、 instanceof
のロジックにより、 prototype
実際にはコンストラクター関数ではなく型を定義します。