163
154

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaScriptの型などの判定いろいろ

Last updated at Posted at 2017-03-14

今更その話題かよ!と思いますが、自分のためのメモとして残しておきます。

#typeof を使う

typeof 演算子はデータ型を示す文字列を返しますが、大きなひとくくりとしての型なので厳密な型判定ができないのはご存知だと思います。

オペランド 結果
undefined "undefined"
null "object"
true / false "boolean"
['array'] "object"
{key: 'value'} "object"
NaN "number"
1234 "number"
new Number() "object"
'abcd' "string"
new String() "object"
function () {} "function"
() => {} "function"
function* () {} "function"
async function () {} "function"
new Promise(() => {}) "object"
Symbol() "symbol"
new Map() "object"
new Set() "object"

null や配列が "object" だったり、普通は使わないけれども new Number()new String()"object" だったりするので、ややこしいです。

ちなみに、'use strict' じゃない時、関数の this に文字列や数値を渡すと "object" になります。

JavaScript
function typeOf01() {
  console.log(typeof this);
}

function typeOf02() {
  'use strict';
  console.log(typeof this);
}

// object になる
typeOf01.call('すっごーい!');  // "object"
typeOf01.call(15820621);       // "object"

// 意図したとおりになる
typeOf02.call('たーのしー!');  // "string"
typeOf02.call(17760704);       // "number"

jQuery.each() でオブジェクトを扱っている場合注意しないといけません。

##typeof だけで判定しても支障がないもの

  • undefined
  • true / false
  • Symbol()

#Object.prototype.toString.call() を使う

Object.prototype.toString.call() を使うと [object 何らかの型] という文字列を取得できて、そこからそのオブジェクトの型がわかるというのもご存知だと思います。

JavaScript
var toString = Object.prototype.toString;
function typeOf(obj) {
  return toString.call(obj).slice(8, -1).toLowerCase();
}
オペランド 結果
undefined "undefined"
null "null"
true / false "boolean"
['array'] "array"
{key: 'value'} "object"
NaN "number"
1234 "number"
new Number() "number"
'abcd' "string"
new String() "string"
function () {} "function"
() => {} "function"
function* () {} "generatorfunction"
async function () {} "asyncfunction"
new Promise(() => {}) "promise"
Symbol() "symbol"
new Map() "map"
new Set() "set"

だいたいのものはこれで十分ですね。NaN など、一部気を付けないといけないものがありますが。

#Object.prototype.toString.call() を使わないで判定

ネイティブで判定できる関数が用意されていたり、他の方法もあるよというものたちです。

##Array

配列の場合、Object.prototype.toString.call() を使うよりもネイティブで実装されている Array.isArray() を使う方が簡単に真偽値を取ることができ、分かりやすいコードになります。
IE8以下では使えません。

JavaScript
Array.isArray([]);           // true
Array.isArray(new Array());  // true
Array.isArray({});           // false
Array.isArray('array');      // false

// こういう方法もあったりした(非推奨)
([] instanceof Array);       // true
({} instanceof Array);       // false

##NaN

NaN は 'Not a Number' と名乗っておきながら Object.prototype.toString.call() でも "number" なのでめんどくさいですね。この判定もネイティブで isNaN()Number.isNaN() が実装されています。
しかし、isNaN()Number.isNaN()違いがあります

###isNaN()

この関数は引数が数値ではないかどうかを判定します。
なので、NaN 以外も true になる場合があります。

JavaScript
isNaN(NaN);              // true
isNaN(undefined);        // true ←!?
isNaN([]);               // false
isNaN({});               // true ←!?
isNaN(null);             // false
isNaN(true);             // false
isNaN(false);            // false
isNaN(1234);             // false
isNaN('');               // false
isNaN('1234');           // false
isNaN('たべないよー!');  // true ←!?

どうしてこのような結果になるのでしょうか。
isNaN()渡された引数を数値に変換できるかどうかを試みる関数なんです。数値に変換できなかったら true を返すわけですね。

###Number.isNaN()

これこそが isNaN() と違い、引数が NaN かどうかを確かめるメソッドになります。
しかし、これは ECMAScript 2015 の仕様なので、最新のブラウザ(ChromeとFirefox)でしか使えません。

JavaScript
Number.isNaN(NaN);              // true
Number.isNaN(undefined);        // false
Number.isNaN({});               // false
Number.isNaN('たべないよー!');  // false

代替(ポリフィル)として以下が使えます。

JavaScript
Number.isNaN = Number.isNaN || function (obj) {
  // typeof NaN === 'number' -> true と、
  // NaN !== NaN -> true を利用する
  return typeof obj === 'number' && obj !== obj;
}

##関数とアロー関数

ECMAScript 2015 からアロー関数 () => {} が使用できるようになりましたが、普通の関数との違いとして 'this' がグローバルなどを指し、'arguments' が取得できない、'prototype' を持たないなど違いがあります。
これの判定も、その性質を利用すれば簡単ですね。(実際にこういうことはしないとは思いますが)

JavaScript
var toString = Object.prototype.toString;

function isFunction(obj, notArrow) {
  return toString.call(obj) === '[object Function]' && (!notArrow || 'prototype' in obj);
}

isFunction(isFunction);            // true
isFunction(function () {});        // true
isFunction(() => {});              // true

isFunction(isFunction, true);      // true
isFunction(function () {}, true);  // true
isFunction(() => {}, true);        // false

// ネイティブなメソッドも false になるかもしれない
isFunction(eval, true);            // false
isFunction(setTimeout, true);      // false

##window

window も実は厄介です。

JavaScript
var toString = Object.prototype.toString;

console.log(typeof window);  // "object"

toString.call(window);
// Chrome: "[object Window]"
// Firefox: "[object Window]"
// Edge: "[object Window]"
// IE11: "[object Window]"
// IE8: "[object Object]"

IE8は切り捨ててしまえとは思いますが、それができない場合はこんな方法があります。

JavaScript
function isWindow(obj) {
  return !!obj && obj === obj.window;
}

isWindow(window);  // true

windowwindow.window があるのを利用したものです。

##DOM

document やHTML要素などのノードを判定するには Object.prototype.toString.call() を使用するより Node.nodeType で行えます。

JavaScript
function isDocument(obj) {
  // document の nodeType は 9
  return !!obj && obj.nodeType === 9;
}

function isElement(obj) {
  // XML/HTML要素の nodeType は 1
  return !!obj && obj.nodeType === 1;
}

isDocument(document);       // true
isDocument(document.body);  // false

isElement(document);        // false
isElement(document.body);   // true

##おまけ:jQueryオブジェクトか調べる

jQueryオブジェクトは Object.prototype.toString.call() を使っても [object Object] になってしまいますが、instanceof 演算子である程度判定できます。

JavaScript
var
  paragraph01 = $('.entry p'),
  paragraph02 = document.querySelectorAll('.entry p'),
  obj = {};

(paragraph01 instanceof jQuery);  // true
(paragraph02 instanceof jQuery);  // false
(obj instanceof jQuery);          // false
163
154
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
163
154

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?