15
5

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.

JavaScript2Advent Calendar 2018

Day 7

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

Last updated at Posted at 2018-12-06

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

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

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

もちろんelifもダメです🐍

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

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

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


const a = [1, 2]

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

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

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


const arr = []

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

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

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

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

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


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

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

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

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

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

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

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

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

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

Lodashの_.isEqual() を使った例

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

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

誤ったNaN判定の例
if(num === NaN){
  console.log('NaNです(?)') 
}

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

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


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

※IEではNumber.isFinite()はサポートされていません。代わりにisFinite()が使用できます。

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

JSでは正確に扱える数の上限が決められています(253 - 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 で割れるか調べればいいだけに思えます。

誤ったチェックの例
  // 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といったプラグインを用いるしかないようです。

BigIntegerを用いた例

#おまけ
中上級者の方のためにJavaScript 真偽判定クイズを作りました。

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

15
5
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
15
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?