typeofとinstanceofの違いを理解する- JavaScript

JavaScriptは変数の型が動的に変化する少し厄介な言語です。例えばとある変数に数値を格納した後、文字列を格納することができます。

var num;
num = 1;
console.log(num); // 1

num = "one";
console.log(num); // "one"

そのため、型に応じた処理を行いたいような場合は、typeofやinstanceofを使って型判定を行います。

typeofはプリミティブ値の型を判定する

typeofは値や変数の型を調べることができる演算子です。ただし、全ての値を判別できるわけではありません。

まずはプリミティブ型のデータをみてみましょう。

var str = "hoge";
var num = 999;
var bln = true;
var nul = null;

console.log(typeof str); // string
console.log(typeof num); // number
console.log(typeof bln); // boolean
console.log(typeof nul); // object
console.log(typeof undefined); // undefined

文字列はstring, 数値はnumber, 真偽はboolean, 未定義の変数はundefinedと表示されます。しかしnullだけは”object”と返されます。

一方でオブジェクト型の場合は以下のようになります。

var obj = {};
var arr = [];
var date = new Date();
var reg = /hoge/;
var err = new Error("hoge");
console.log(typeof obj); // object
console.log(typeof arr); // object
console.log(typeof date); // object
console.log(typeof reg); // object
console.log(typeof err); // object

オブジェクト型の場合は”object”と表示されます。ただし、すべてのオブジェクトを”object”と表示してくれればいいのですが、Functionオブジェクトだけは”function”と表示されます。

var fnc = function(){};
console.log(typeof fnc); // function

このようにtypeofは値によって正しい型名を返してくれる場合もあれば、???といった場合もあります。なので、typeofにすべての型判定を委ねるのは厄介で、

「typeof演算子はプリミティブ型の文字列、数値、真偽値を判定する場合に有効」

であることを意識する必要があります。

instanceofでオブジェクトを判定する

typeofでオブジェクトを判定しても”object”としか出てきませんでした。そこでオブジェクトが何者なのか?を判定するにはtypeofではなくinstanceofを使います。

typeofは型名を返しましたが、instanceofは「オブジェクトがどのコンストラクタから生成されたか」を判定します。

var obj = {};
var arr = [];
var fnc = function() {};
var date = new Date();
var reg = /hoge/;
var err = new Error('hoge');

console.log(obj instanceof Object); // true
console.log(arr instanceof Array); // true
console.log(fnc instanceof Function); // true
console.log(date instanceof Date); // true
console.log(reg instanceof RegExp); // true
console.log(err instanceof Error); // true

このようにinstanceofに判定したい型名を指定することで、オブジェクトの型を判定することができます。

Object.prototype.toString.call()で型判定を行う

ここまでtypeofとinstanceofの挙動をみてきましたが、プリミティブ型やオブジェクト型を意識して使い分けるのは面倒です。

そこでObject.prototype.toString.call()を使うと、この悩みが解決されます。

var str = 'hoge';
var num = 999;
var bln = true;
var nul = null;
var obj = {};
var arr = [];
var fnc = function() {};
var date = new Date();
var reg = /hoge/;
var err = new Error('hoge');

Object.prototype.toString.call(str); // [object String]
Object.prototype.toString.call(num); // [object Number]
Object.prototype.toString.call(bln); // [object Boolean]
Object.prototype.toString.call(nul); // [object Null]
Object.prototype.toString.call(undefined); // [object Undefined]
Object.prototype.toString.call(obj); // [object Object]
Object.prototype.toString.call(arr); // [object Array]
Object.prototype.toString.call(fnc); // [object Function]
Object.prototype.toString.call(date); // [object Date]
Object.prototype.toString.call(reg); // [object RegExp]
Object.prototype.toString.call(err); // [object Error]

判定結果は“[object 型名]”という文字列で返ってきます。ちょっと冗長な情報が付加されますが、それぞれの型を判別することができます。プリミティブ型・オブジェクト型を意識する必要もありません。

ただし、意識する必要がないということは、文字列などプリミティブ型(“string”)・オブジェクト型(new String();)の両方で表現できる場合は、同じ結果を返すことに注意が必要です。

var strPrm = "hoge";
var strObj = new String("hoge");

Object.prototype.toString.call(strPrm); // [object String]
Object.prototype.toString.call(strObj); // [object String]

Related Posts