JavaScript の真偽判定でハマりやすいポイント 7 選

初心者の方が JS で真偽判定はする際にハマりやすい点を、ごくごく基本的なものから順に 7 つまとめました。

※ 記事の最後に**中上級者向けのクイズ**もあるので、是非プレイしてみてください。

#1. else if のスペース
まずは基本中の基本からですが、他言語のように`elseif`と詰めて書くと `SyntaxError` になります。

もちろん`elif`もダメです🐍

#2. 配列かどうかの判定

文字列や数値であれば`typeof`で判定できるのですが、配列の場合は 'object' と判定されてしまうので使えません。

そのため`Array.isArray()`を使います。

```javascript

const a = [1, 2]

console.log(typeof a) // => 'object'
console.log(Array.isArray(a)) // => true 
```

#3. 配列が空かどうかの判定
文字列とは異なり、配列は空でも真と扱われるため`!array`のようには判定できません。

そのため配列の長さが 0 かどうかで判定します。

```javascript

const arr = []

console.log(!arr) // => false
console.log(!arr.length) // => true 

if(!arr.length) {
  console.log('空です')
}
```

#4. オブジェクトが空かどうかの判定

配列と同様、空のオブジェクトも真として扱われます。
さらにオブジェクトには length プロパティがありません。

そのため、`Object.keys(obj)`で全てのキーを配列で取得し、
その配列の長さが 0 かどうかをみます。

```javascript

const obj = {key1: 1, key2: 2}

console.log(Object.keys(obj)) // => ["key1", "key2"]

if( Object.keys(obj).length ) {
  console.log('空じゃないよ')
}
```

#5. 配列やオブジェクトが等しいことの判定

配列やオブジェクトの**中身**が等しいことの判定に`===`を用いることはできません。
実際、以下の式は`false`になります。

```javascript
const a = [1, 2]
const b = [1, 2]
console.log(a === b) // => false
```

確かに`a`から要素を削除しても`b`は何も変化しないですし、両者を「異なる対象」と判定するのは妥当と言えます。

では、両者の中身が等しいかを判定するにはどうすればいいのかというと、どうも簡単かつ確実な方法は用意されてないようです。

自力で判定用関数をゴリゴリ作ってもいいですが、ササっとやりたい場合はLodashなどのプラグインを使うのがよさそうです。

[Lodashの_.isEqual() を使った例](https://jsfiddle.net/shin2357/u2mko40x/)

#6. 普通の数値かどうかの判定
つまり`Infinity`でも`NaN`でもない、普通の数であるかを判定したい場合です。

次の`NaN`判定コードは想定通りには動きません。

```javascript:誤ったNaN判定の例
if(num === NaN){
  console.log('NaNです（？）') 
}
```

なぜかというと JS の仕様では`NaN === NaN`は`false`だからです。
（というか`NaN`はいかなる値と比較しても`false`となります）

なので`Number.isFinite()`を使います。
`Number.isFinite(aug)`は、`aug`が`±Infinity`および`NaN`でない数値の場合のみ`true`を返してくれます。

```javascript

if( Number.isFinite(num) ) {
  console.log('numは普通の数値です')
}
```

※IEでは`Number.isFinite()`はサポートされていません。代わりに[`isFinite()`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/isFinite)が使用できます。

#7. 大きな数に関する判定

JSでは正確に扱える数の上限が決められています（2<sup>53</sup> - 1 = 9007199254740991）。
この上限を超えた数を使った計算は、正確性が保証されません。

計算中にこの上限を超過し、数値が不正確な値に変化してもエラーは出ないため、意図しない動作が生じえます。

###実際的な例
以下は、私がエンジニア1年目のころに経験した例（を単純化したもの）です。

まず、次のような仕様の IC カードがあるとします。

- 各カードには 16 桁の ID が振られている（例：319A-F3D5-EAE2-55E7）
- 正しい形式の ID は、16 進数の整数に変換したとき 10 で割り切れる。

例えば、
`ID: 319A-F3D5-EAE2-55E7`
からハイフンを除去し、16進法としてみると、
`319AF3D5EAE255E7 `
になりますが、これは 10 進法で 
`3574437354366195175`
で 10 で割り切れないため、不正な ID であることがわかります。

一見すると、このカード ID の正当性をチェックするには、ID を`parseInt()`で変換して 10 で割れるか調べればいいだけに思えます。

```javascript:誤ったチェックの例
  // id取得、ハイフン除去などは省略

  console.log(id) // => "319AF3D5EAE255E7"
  const num = parseInt(id, 16)

  if(num % 10 !== 0) {
    throw new Error('不正なIDです') 
  }
```

しかし:point_up:のコードをテストをしてみると、どんな ID もチェックを通り抜けてしまい、うまくいきません。

これはなぜかというと、16 桁の ID を`parseInt()`で変換したとき値が大きくなりすぎて、下何桁かが 0 に丸められてしまうためです。

最下桁が0に丸められた結果、必ず10で割り切れるようになるため、常に「正しい形式」だと判定されてしまうわけです。

###大きな数を扱うためには

将来的には大きな数が扱える BigInt が仕様に入るみたいですが、2018年12月現在では[BigInteger.js](https://github.com/peterolson/BigInteger.js)といったプラグインを用いるしかないようです。

[BigIntegerを用いた例](https://jsfiddle.net/shin2357/875jdhay/)




#おまけ
中上級者の方のために[JavaScript 真偽判定クイズ](https://quiz-maker.site/quiz/play/gASJdm20181124135915)を作りました。

上で挙げなかった細かい内容も含まれているので、ぜひプレイしてみてください。