かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JS 0や文字列を含んだ数字のチェックをしたいメモ

数値を扱う時や数値変換を行う時に0が鬼門になることが多いのでメモ

var checklist = [
  0,
  "0",
  "",
  undefined,
  "undefined",
  false,
  "false",
  true,
  "true",
  null,
  "null",
  NaN,
  "NaN",
  Infinity,
  "Infinity",
  -Infinity,
  "-Infinity"
];

-0で数値変換した場合

checklist.forEach(function(v) {
  console.log(v + " (" + typeof(v) + ") =>", v-0);
});

出力👇

0 (number) => 0
"0" (string) => 0
"" (string) => 0
undefined (undefined) => NaN
"undefined" (string)  => NaN
false (boolean)  => 0
"false" (string) => NaN
true (boolean)  => 1
"true" (string) => NaN
null (object)   => 0
"null" (string) => NaN
NaN (number)   => NaN
"NaN" (string) => NaN
Infinity (number)   => Infinity
"Infinity" (string) => Infinity
-Infinity (number)   => -Infinity
"-Infinity" (string) => -Infinity

isNaN() と Number.isNaN()

isNaN()Number.isNaN() とでは判定結果が異なる。
isNaN() は暗黙の型変換でNaNになるものも含まれるっぽい。

isNaN()

checklist.forEach(function(v) {
  if( isNaN(v) ) {
    console.log('isNaN >', v, "("+typeof(v)+")");
  }
});

出力👇

isNaN > undefined (undefined)
isNaN > "undefined" (string)
isNaN > "false" (string)
isNaN > "true" (string)
isNaN > "null" (string)
isNaN > NaN (number)
isNaN > "NaN" (string)

Number.isNaN() (ES6)

checklist.forEach(function(v) {
  if( Number.isNaN(v) ) {
    console.log('Number.isNaN >', v, "("+typeof(v)+")");
  }
});

出力👇

Number.isNaN >  NaN (number)

isFinite() と Number.isFinite()

isNaNと同様で判定結果が異なる。
isNaNと同じくglobalにある isFinite() は暗黙の型変換で判定してるっぽい。

isFinite()

checklist.forEach(function(v) {
  if( isFinite(v) ) {
    console.log('isFinite >', v, "("+typeof(v)+")");
  }
});

出力👇

isFinite > 0 (number)
isFinite > "0" (string)
isFinite > "" (string)
isFinite > false (boolean)
isFinite > true (boolean)
isFinite > null (object)

""(空文字列) true, false, null はすり抜けてしまう。

Number.isFinite() (ES6)

checklist.forEach(function(v) {
  if( Number.isFinite(v) ) {
    console.log('Number.isFinite >', v, "("+typeof(v)+")");
  }
});

出力👇

Number.isFinite > 0 (number)

"0", "123"のような文字列化しているものは弾かれてしまう。

Number.isInteger() (ES6)

型チェック込で整数かどうかを判別するので、少数や文字列になっているものも弾かれる。

Number.isFinite("0");   // false
Number.isFinite(12.8); // false

数値と文字列化している数字のみチェックしたい

var checklist = [
  0,
  "0",
  "",
  undefined,
  "undefined",
  false,
  "false",
  true,
  "true",
  null,
  "null",
  NaN,
  "NaN",
  Infinity,
  "Infinity",
  -Infinity,
  "-Infinity",
  "123",
  -5,
  12.8,
  "A"
];

var a = checklist.filter(function(val, i) {
  // 文字列になった数字も取り出したいので、isFinite() を使う
  // true, false がすり抜けてしまうので、`boolean` 型も弾く
  if( !isFinite(val) || typeof(val) === "boolean" ) {
    return false;
  }
  if( val-0 === 0 ) {
    // 暗黙の型変換で 0 になるものは 数字で構成されているかチェックする
    if(/[0-9]/g.test(val)) return true;
  } else {
    return true;
  }
});

console.log(a); // [ 0, '0', '123', -5, 12.8 ]

暗黙の型変換で0になるものだけ正規表現でチェックをしているので、そこまで実行速度に影響は無いとは思います。

条件式版も作ってみましたが、条件がこれでOKかちょっと不安があります…

var a = checklist.filter(function(val, i) {
  // 文字列になった数字も取り出したいので、isFinite() を使う
  // true, false がすり抜けてしまうので、`boolean` 型も弾く
  if( !isFinite(val) || typeof(val) === "boolean" ) {
    return false;
  }
  // 条件は本当にコレで大丈夫???
  if( typeof(val) === 'number'
   || (typeof(val) === 'string' && val !== "")
   || val - 0 !== 0
  ) {
    return true;
  }
});

console.log(a); // [ 0, '0', '123', -5, 12.8 ]

感想

なんとか数字と文字列化した数字を判定することができました。
ゆるふわ型javascriptの暗黙の型変換ほんと鬼門です。
そう考えるとRubyfalsenilのときだけfalseになる。って凄く便利なんですね!


[参考]