JavaScriptでは、undefinedの大小比較の結果は常にfalseです。
undefined > 0 // false
undefined >= 0 // false
undefined < 0 // false
undefined <= 0 // false
undefined同士の大小比較でも常にfalseです。
undefined > undefined // false
undefined >= undefined // false
undefined < undefined // false
undefined <= undefined // false
なぜなのか、仕様を確認してみましょう。
大小比較 共通手順
大小比較のアルゴリズムは、Abstract Relational Comparisonの章に記載があります。
x < yはtrue、falseまたはundefinedを返します。undefinedが返る時は、少なくとも片方がNaN(非数)であることを表します。
x < yの判定のうち、片方(または両方)がundefinedのときのステップを中心に、以下に記載します。
-
xをプリミティブ型(Boolean,Null,Undefined,Number,BigInt,String,Symbolのいずれか)に変換する -
yをプリミティブ型(Boolean,Null,Undefined,Number,BigInt,String,Symbolのいずれか)に変換する - 変換した結果、両方ともStringであれば、
-
xとyを辞書順で並べ、xの方が先に来るならばtrue、そうでないならばfalseを返す(詳しくは仕様を参照してください)
-
- それ以外であれば、
-
xをさらにNumberに変換する -
yをさらにNumberに変換する -
xが``NaNであればundefined`を返す -
yが``NaNであればundefined`を返す -
xとyを数値として比較し、xの方が小さければtrue、そうでないならばfalseを返す(詳しくは仕様を参照してください)
-
undefinedの大小比較 共通手順を追う
例として、undefined < 0のときの処理を追ってみましょう。
上の手順に当てはめると、x = undefined、y = 0です。
-
xをプリミティブ型(Boolean,Null,Undefined,Number,BigInt,String,Symbolのいずれか)に変換する
→undefinedをプリミティブ型に変換するとundefined(変化なし)です。 -
yをプリミティブ型(Boolean,Null,Undefined,Number,BigInt,String,Symbolのいずれか)に変換する
→0をプリミティブ型に変換すると0(変化なし)です。 - 変換した結果、両方ともStringであれば、
→ 条件に当てはまらないので次に進みます - それ以外であれば、
-
xをさらにNumberに変換する -
yをさらにNumberに変換する
→Numberへの変換表はこちらです。undefinedはNaNに変換され、0は0のままです。
-
| データ型 | 返却値 |
|---|---|
| Undefined | NaN |
| Null | +0 |
| Boolean | trueならば1、falseならば+0 |
| Number | 元の値 |
| String | 文字列が数字ならばその数字、それ以外はNaN。詳細はToNumber Applied to the String Typeを参照 |
| Symbol | TypeError exceptionを投げる |
nullやtrue、falseが数値に変換されるのも興味深いです。これらに言及した記事を参考資料としてリンクしました。
次に、 3. xがNaNであればundefinedを返す
→ xの値を変換した結果がNaNなので、undefinedが返されます。
さて、ここまででundefined < 0の評価値はundefinedであることがわかりました。
しかし、実際はundefined < 0の評価結果はfalseです。何が間違っているのでしょうか。
それを解き明かすには、もうひとつの仕様を読む必要があります。
大小比較の実行手順
大小比較の実行手順はRuntime Semantics: Evaluationに記載があります。
主要な部分だけ抜粋します。
x < yの評価方法は、
- 大小比較 共通手順で
x < yを評価する - 結果が
undefinedであればfalse、それ以外であれば評価結果を返す
x > yの評価方法は、
- 大小比較 共通手順で
y < xを評価する - 結果が
undefinedであればfalse、それ以外であれば評価結果を返す
x <= yの評価方法は、
- 大小比較 共通手順で
y < xを評価する - 結果が
trueまたはundefinedであればfalse、それ以外であればtrueを返す
x >= yの評価方法は、
- 大小比較 共通手順で
x < yを評価する - 結果が
trueまたはundefinedであればfalse、それ以外であればtrueを返す
undefinedの大小比較の実行手順を追う
undefined < 0のときの処理を追ってみましょう。
x < yの評価方法を見ます。
- 大小比較 共通手順で
x < yを評価する
→ 上で確認したように、undefined < 0の評価結果はundefinedです。 - 結果が
undefinedであればfalse、それ以外であれば評価結果を返す
→undefinedなので、falseが返ります。
x > y、x <= y、x >= yのそれぞれについても、undefined < 0の結果がundefinedなので、全ての比較においてfalseが返ることがわかりました。
x < yがfalseだからといって、x >= yが必ずしもtrueにならないことがあるのですね。
参考資料
- Relational Operators
- Abstract Relational Comparison
- JavaScriptの大小比較は数値に変換される - Qiita
- JavaScriptのプリミティブへの変換を完全に理解する - Qiita
- Nullの奇妙な比較、あるいはJavaScriptの仕様を読むことが重要な理由 - Qiita
あとがき
意外にも、undefinedの大小比較についての記載が見当たらず苦労しました。
数値同士や文字列同士以外を比較するようなコードを、実際に仕事で書くことがあるとしたら多分アプリの設計ミスなのですが、undefinedが紛れ込むことはあるかなーと思い、気をつけようと思いました。