JavaScriptのnull/undefined判定の仕方いろいろ
なんの記事?
JavaScript で null
/undefined
判定の書き方がいろいろあってコードレビューとか説明するとき用に改めてまとめてみた。
どちらかというと普段 JavaScript をあまり書かない人が読むことを想定して書く予定。
前提知識
JavaScript では Boolean に変換したときに true
/ false
になる値のことをそれぞれ Truthy (真値) / Falsy (偽値) と呼ぶ。
Truthy/Falsyを利用する時の罠
null
/undefined
判定のとき以下のように書きたくなるが、特定のケースで意図した通りに動かなかったり、それを考慮したコードレビューが大変だったりする。
let unitPrice
const formatPrice = (num) => { console.log('run formatPrice') }
// unitPrice が null/undefined のときは formatPrice() を実行しないようにしたい
unitPrice = 100
if (unitPrice) { formatPrice(unitPrice) }
// => 意図通り formatPrice() は実行される
unitPrice = 0
if (unitPrice) { formatPrice(unitPrice) }
// => 0 が Falsy のため実行されない(null/undefined ではないで実行してほしかった)
なので、本記事では if (value)
を使わないパターンのみまとめた。
※論理否定演算子 (!) を使う if (!value)
についても同じく除外
先におすすめの書き方
if文
// null/undefined なら実行する
if(unitPrice == null) { console.log('unit price is null/undefined') }
// null/undefined 以外なら実行する
if(unitPrice != null) { formatPrice(unitPrice) }
代入
// null/undefined ならデフォルト値(0)を代入
const price = unitPrice ?? 0
null
/undefined
判定の仕方いろいろ
本題:
value === null || value === undefined
厳密等価演算子 (===)を使って書くパターン。
// null/undefined なら実行する
if (unitPrice === null || unitPrice === undefined) { console.log('unit price is null/undefined') }
// null/undefined 以外なら実行する
if (unitPrice !== null && unitPrice !== undefined) { formatPrice(unitPrice) }
シンプルで読みやすいが、ちょっと長いのが難点。
長さについては好みの部分もあるので、明示的に書くことを重視するならベターかも。
value == null
厳密等価演算子 (===)ではなく等価演算子 (==)を使って書くパターン。
// null/undefined なら実行する
if(unitPrice == null) { console.log('unit price is null/undefined') }
// null/undefined 以外なら実行する
if(unitPrice != null) { formatPrice(unitPrice) }
等価演算子 (==)は比較する値同士の型が違う場合は型変換をしてみてから比較するという仕様のため、'0'
と 0
のように型が違う値を等価だと判定してしまって困るなどの罠があり、基本的には使わない方がよい。
ただし、前述の利用して == null
のように書くときは例外的に利用することがある。
なぜ == null
で null
/undefined
を両方判定できるかの説明はややこしいので省くが、簡潔に言うと null
と undefined
は型が異なるが値としては同じなので、値だけをみる等価演算子 (==)では null
/undefined
両方の判定ができる。(なので、== undefined
と書いても同じ)
この辺りはあまり詳しく書いている公式ドキュメントが見つからなかったが、null と undefined の違いあたりを読んでもらうと分かるかもしれない。
どういう仕組みか知っていないと正しく読めないので、このパターンは使わないというルールにしている場合もありそう。
value ?? 'default value'
Null 合体演算子 (??)を使って書くパターン。
// null/undefined ならデフォルト値(0)を代入
const price = unitPrice ?? 0
Null 合体演算子 (??)は左辺が null
/undefined
の場合に右辺の値を返し、それ以外の場合に左辺の値を返します。そのため、右辺にデフォルト値を書くことで null
/undefined
の場合のみデフォルト値を返すときに利用できる。
論理和演算子 (||)を使ったパターンとの違い
注意1:このパターンは一見すると論理和演算子 (||)を使った書き方に似ているが、前述の通りnull
/undefined
かどうかの判定のみをしてくれるので、Truthy/Falsy の罠にかからない。
// null/undefined ならデフォルト値(0)を代入
const price = unitPrice || 0
// => unitPrice が Truthy の場合も unitPrice が代入されてします(例:unitPrice = '0')
↑ のパターンは TypeScript を使っていて unitPrice
が Number 型であることが分かっているケースなどでは基本的に問題にならないが、以下のようなケースもあるため、個人的にはNull 合体演算子 (??)を使った書き方に統一するのが望ましいと思う。
// null/undefined ならデフォルト値(空文字)を代入
const price = unitPrice || ''
// => unitPrice が 0 のときに price が空文字になってしまう
補足:typescript-eslint
に prefer-nullish-coalescing というルールがある。
注意2:if 文の中で使うと Truthy/Falsy の罠にかかる可能性あり
// Bad: null/undefined なら実行する
if (unitPrice ?? true) { console.log('unit price is null/undefined') }
// => unitPrice が Truthy の場合も実行されてしまう(例:unitPrice = 100)
// Bad: null/undefined 以外なら実行する
if (unitPrice ?? false) { formatPrice(unitPrice) }
// => unitPrice が Falsy の場合に実行されない(例:unitPrice = 0)
おわり
ややこしい仕様も乗り越えて、よき JavaScript ライフを!
Discussion
みたいなのが好きなのですが、これだと TypeScript 換算で value が null / undefined であることが type としてなぜか保証されない ので つらいがある。(jsで触る分には保証されるのだが vscode の型判定は typescript 由来なのでつらみがある
等価演算子のMDNのページの、「抽象等価比較アルゴリズム」のリンクがECMA-262 5.1のアルゴリズムが書かれている箇所へのリンクです。
最新のECMA-262 2024だと、13.11 Equality Operators - 13.11.1 Runtime Semantics: Evaluationのアルゴリズム中で参照されている、
IsLooselyEqual
が詳しい箇所になります。