JavaScriptでundefinedの大小比較はつねにfalse。その理由を、仕様を引用して解説する

JavaScriptでは、``undefined``の大小比較の結果は常に`false`です。

```javascript
undefined > 0 // false
undefined >= 0 // false
undefined < 0 // false
undefined <= 0 // false
```

``undefined``同士の大小比較でも常にfalseです。

```javascript
undefined > undefined // false
undefined >= undefined // false
undefined < undefined // false
undefined <= undefined // false
```

なぜなのか、仕様を確認してみましょう。


# 大小比較 共通手順
大小比較のアルゴリズムは、[Abstract Relational Comparison](http://www.ecma-international.org/ecma-262/6.0/#sec-abstract-relational-comparison)の章に記載があります。

> `x < y`は`true`、`false`または`undefined`を返します。`undefined`が返る時は、少なくとも片方が`NaN`(非数)であることを表します。

`x < y`の判定のうち、片方(または両方)が`undefined`のときのステップを中心に、以下に記載します。

1. `x`をプリミティブ型(`Boolean`, `Null`, `Undefined`, `Number`, `BigInt`, `String`, `Symbol`のいずれか)に変換する
2. `y`をプリミティブ型(`Boolean`, `Null`, `Undefined`, `Number`, `BigInt`, `String`, `Symbol`のいずれか)に変換する
3. 変換した結果、両方ともStringであれば、
    1. `x`と`y`を辞書順で並べ、`x`の方が先に来るならば`true`、そうでないならば`false`を返す(詳しくは仕様を参照してください)
4. それ以外であれば、
    1. `x`をさらに`Number`に変換する
    2. `y`をさらに`Number`に変換する
    3. `x`が``NaN`であれば`undefined`を返す
    4. `y`が``NaN`であれば`undefined`を返す
    5. `x`と`y`を数値として比較し、`x`の方が小さければ`true`、そうでないならば`false`を返す(詳しくは仕様を参照してください)

# undefinedの大小比較 共通手順を追う
例として、`undefined < 0`のときの処理を追ってみましょう。
上の手順に当てはめると、`x = undefined`、`y = 0`です。

1. `x`をプリミティブ型(`Boolean`, `Null`, `Undefined`, `Number`, `BigInt`, `String`, `Symbol`のいずれか)に変換する<br/>→ `undefined`をプリミティブ型に変換すると`undefined`(変化なし)です。
2. `y`をプリミティブ型(`Boolean`, `Null`, `Undefined`, `Number`, `BigInt`, `String`, `Symbol`のいずれか)に変換する<br/>→ `0`をプリミティブ型に変換すると`0`(変化なし)です。
3. 変換した結果、両方ともStringであれば、<br/>→ 条件に当てはまらないので次に進みます
4. それ以外であれば、
    1. `x`をさらに`Number`に変換する
    2. `y`をさらに`Number`に変換する<br/>→ `Number`への変換表はこちらです。`undefined`は`NaN`に変換され、`0`は`0`のままです。

|データ型|返却値|
|---|---|
|Undefined|NaN|
|Null|+0|
|Boolean|trueならば1、falseならば+0|
|Number|元の値|
|String|文字列が数字ならばその数字、それ以外は`NaN`。詳細は[ToNumber Applied to the String Type](http://www.ecma-international.org/ecma-262/6.0/#sec-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](http://www.ecma-international.org/ecma-262/6.0/#sec-relational-operators-runtime-semantics-evaluation)に記載があります。

主要な部分だけ抜粋します。

`x < y`の評価方法は、
1. 大小比較 共通手順で`x < y`を評価する
2. 結果が`undefined`であれば`false`、それ以外であれば評価結果を返す

`x > y`の評価方法は、
1. 大小比較 共通手順で`y < x`を評価する
2. 結果が`undefined`であれば`false`、それ以外であれば評価結果を返す

`x <= y`の評価方法は、
1. 大小比較 共通手順で`y < x`を評価する
2. 結果が`true`または`undefined`であれば`false`、それ以外であれば`true`を返す

`x >= y`の評価方法は、
1. 大小比較 共通手順で`x < y`を評価する
2. 結果が`true`または`undefined`であれば`false`、それ以外であれば`true`を返す

# undefinedの大小比較の実行手順を追う
`undefined < 0`のときの処理を追ってみましょう。
`x < y`の評価方法を見ます。
1. 大小比較 共通手順で`x < y`を評価する<br/>→ 上で確認したように、`undefined < 0`の評価結果は`undefined`です。
2. 結果が`undefined`であれば`false`、それ以外であれば評価結果を返す<br/>→ `undefined`なので、`false`が返ります。

`x > y`、`x <= y`、`x >= y`のそれぞれについても、`undefined < 0`の結果が`undefined`なので、全ての比較において`false`が返ることがわかりました。

`x < y`が`false`だからといって、`x >= y`が必ずしも`true`にならないことがあるのですね。

# 参考資料
- [Relational Operators](http://www.ecma-international.org/ecma-262/6.0/#sec-relational-operators)
- [Abstract Relational Comparison](http://www.ecma-international.org/ecma-262/6.0/#sec-abstract-relational-comparison)
- [JavaScriptの大小比較は数値に変換される - Qiita](https://qiita.com/krmbn0576/items/cc51dfc7a44eae1dfc24)
- [JavaScriptのプリミティブへの変換を完全に理解する - Qiita](https://qiita.com/uhyo/items/44c2f79873de13186743)
- [Nullの奇妙な比較、あるいはJavaScriptの仕様を読むことが重要な理由 - Qiita](https://qiita.com/rana_kualu/items/24f5c037056089ff161a)

# あとがき
意外にも、`undefined`の大小比較についての記載が見当たらず苦労しました。
数値同士や文字列同士以外を比較するようなコードを、実際に仕事で書くことがあるとしたら多分アプリの設計ミスなのですが、`undefined`が紛れ込むことはあるかなーと思い、気をつけようと思いました。