Nullの奇妙な比較、あるいはJavaScriptの仕様を読むことが重要な理由

以下は[Javascript : The Curious Case of Null >= 0](https://blog.campvanilla.com/javascript-the-curious-case-of-null-0-7b131644e274)の日本語訳です。

# Why it's important to read the Javascript Spec

![1-F-EDLK-OugJ_4KOgtJqnPA.png](https://qiita-image-store.s3.amazonaws.com/0/26088/b44bf056-eb69-af13-1c71-8be70c6204f9.png)

JavaScriptの速成コースで作ったソースをコンパイルしていたところ、nullの挙動について面白い現象を発見したので周囲に尋ねてみた。

```javascript
	null > 0;  // false
	null == 0; // false
	
	null >= 0; // true
```

ちょっと待って、これは何？
全くもってナンセンスだ。

いったいどのような値が、0より大きくなく、0と等しくもなく、0と等しいかそれより大きい状態になるというのだ？

当初、私はこれはJavaScriptだから仕方ないとスルーしていたが、しかしこの動作はとても奇妙で、ついつい興味をそそられてしまった。
null型とその扱いにどのような関係があるのか、また関係性と等価性のチェックはどのように行われるのだろうか？

そこで、私はこの現象の根本的原因を突き止めるため、JavaScriptの信頼できる唯一の情報源を探すことにした。
つまり、JavaScriptの仕様だ。

そして私は幻覚的な体験を経験したのだった。


# The Abstract Relational Comparison Algorithm

まずは比較アルゴリズムを見てみよう。

```javascript
	null > 0; // false
```

仕様によると、演算子`>`と`<`は、[Abstract Relational Comparison Algorithm](http://interglacial.com/javascript_spec/a-11.html#a-11.8.5)というアルゴリズムに従って文が`true`か`false`かを返す。
以下は`x < y`の比較アルゴリズムである。

```
1. ToPrimitive(x, Number)を呼んでxをプリミティブ型にする。
2. ToPrimitive(y, Number)を呼んでyをプリミティブ型にする。
3. xの型とyの型が共にStringであれば、16に進む。
4. ToNumber(x)を呼んでxをNumber型にする。
5. ToNumber(y)を呼んでyをNumber型にする。
6. xがNaNであれば`undefined`を返して終了。
7. yがNaNであれば`undefined`を返して終了。
8. xとyの値が同じであればfalseを返して終了。
9. xが+0でyが-0であればfalseを返して終了。
10. xが-0でyが+0であればfalseを返して終了。
11. xが+∞であればfalseを返して終了。
12. yが+∞であればtrueを返して終了。
13. yが-∞であればfalseを返して終了。
14. xが-∞であればtrueを返して終了。
15. xの数値がyの数値より小さければtrueを返して終了。それ以外ならfalseを返して終了。この時点でxy両方とも無限大でも0でもない値であることが保証されている。
16. yの値がxの値のprefixである場合、falseを返して終了。(文字列qが文字列pと文字列rをk連結した結果である場合、pをqのprefixと呼ぶ。rは空文字の可能性もあり、任意の文字列はそれ自身のprefixである。)
17. xの値がyの値のprefixである場合、trueを返して終了。
18. xとyの文字列を順に見ていき、最初に異なる文字が現れる位置kを見付ける。(両者がそれぞれのprefixではないので、そのような数kは必ず存在する。)
19. xのk番目の文字のコードポイントの値をmとする。
20. yのk番目の文字のコードポイントの値をnとする。
21. m < nであればtrueを返す。それ以外ならfalseを返す。
```

今回の文`null > 0`で考えてみよう。
ステップ1と2で、nullと0それぞれにToPrimitiveを呼んでプリミティブ型に変換しようとしている。
ToPrimitiveの結果は以下のようになる。

|入力の型  |出力  |
|---|---|
|Undefined  |変更なし  |
|Null  |変更なし  |
|Boolean  |変更なし  |
|Number  |変更なし  |
|String  |変更なし  |
|Object  |デフォルト値を返す。Objectのデフォルト値はobject型の内部メソッドDefaultValueによって決められ、第2引数は無視される。 |

変換テーブルに従うと、nullも0もどちらも変換されない。
よってステップ3には当てはまらず、ステップ4と5で両辺がNumberへと変換される。
Numberへの変換は以下の表のようになっている。

|入力の型  |出力  |
|---|---|
|Undefined  |NaN  |
|Null  |+0  |
|Number  |変更なし  |
|Boolean  |trueなら1、falseなら+0  |
|...  |...  |

StringとObjectは今回無関係であるため省略した。
詳細は[ここ](http://interglacial.com/javascript_spec/a-9.html#a-9.3)から確認できる。

nullは+0となり、0は0のままになる。
どちらの値もNaNではないのでステップ6と7はスキップする。
ステップ8で+0と0は等しいと評価され、アルゴリズムはfalseを返すこととなる。
以上より、

```javascript
	null > 0; // false
	null < 0; // false
```

両方ともfalseになる。


# The Abstract Equality Comparison Algorithm

次に進もう。

```javascript
	null == 0; //false
```

これは興味深い結果だ。
`==`演算子は[Abstract Equality Comparison Algorithm](http://interglacial.com/javascript_spec/a-11.html#a-11.9.3)というアルゴリズムに従い、`true`か`false`を返す。

```
1. xとyの型が異なる場合、14に進む。
2. xの型がundefinedであればtrueを返して終了。
3. xの型がNullであればtrueを返して終了。
4. xの型がNumberでない場合、11に進む。
5. xがNaNであればfalseを返して終了。
6. yがNaNであればfalseを返して終了。
7. xとyが同じ数値であればtrueを返して終了。
8. xが+0でyが-0であればtrueを返して終了。
9. xが-0でyが+0であればtrueを返して終了。
10. falseを返して終了。
11. xの型がStringである場合、xとyの文字列が全く同じであればtrueを返して終了。それ以外ならfalseを返して終了。
12. xの型がBooleanである場合、xとyの両方がtrueか、両方がfalseであればtrueを返して終了。それ以外ならfalseを返して終了。
13. xとyが同じObjectを参照しているか、結合オブジェクトである場合、trueを返して終了。それ以外ならfalseを返して終了。
14. xがnullでyがundefinedであればtrueを返して終了。
15. xがundefinedでyがnullであればtrueを返して終了。
16. xの型がNumberでyの型がStringの場合、x==ToNumber(y)の結果を返して終了。
17. xの型がStringでyの型がNumberの場合、ToNumber(x)==yの結果を返して終了。
18. xの型がBooleanの場合、ToNumber(x)==yの結果を返して終了。
19. yの型がBooleanの場合、x==ToNumber(y)の結果を返して終了。
20. xの型がStringかNumberでyの型がObjectの場合、x==ToPrimitive(y)の結果を返して終了。
21. xの型がObjectでyの型がStringかNumberの場合、ToPrimitive(x)==yの結果を返して終了。
22. falseを返して終了。
```

nullと0の比較では、まず型が異なるためステップ1からステップ14に飛ぶ。
nullの型はNullなので、ステップ14から21までは全て当てはまらない。
最終的にステップ22まで進み、結果はデフォルト値のfalseとなる。
従って、

```javascript
	null == 0; //false
```

となる。

# The Greater-than-or-equal Operator (>= )

最後に問題のこれをチェックしていこう。

```javascript
	null >= 0; // true
```

これは仕様による理解を完全に放棄したところだ。

高度に政治的な判断によると、関係演算子`>=`は以下のように評価される。

```javascript
	null < 0 がfalseであれば、null >= 0 がtrueになる
```

Really？

従って、

```javascript
	null >= 0; // true
```

実のところ、この結果は理に叶っている。
数学的には、2数xyがあって、xがyより小さくないのであれば、xはyより大きいかyと同じでないといけないからだ。

この式の評価は、最適化のため以下のように行われたのではないかと考えている。
まず`x>y`を評価する。
これがfalseだった場合、次に`x==y`を評価する。
それでもなければ最後に`x<y`を評価しなければならないところだが、`x>y`の結果から即trueを返しているのではないか。

( `>=`演算子の正確な評価は、[ここ](http://interglacial.com/javascript_spec/a-11.html#a-11.8.4)で見付けることができる )

・・・

このように些細な疑問でも、答えを調べていく過程でいくつもの新しい発見がありました。
この記事が貴方の助けになることを願っています。

・・・

もっと助けが必要？
興味があるなら[Twitter](https://twitter.com/AbinavSeelan)や[Github](https://github.com/abinavseelan/)で活動しています。


# まとめ

[`>=`の評価](http://interglacial.com/javascript_spec/a-11.html#a-11.8.4)の項目には、
【`x<y`がtrueかundefinedならfalseを返し、それ以外ならtrueを返す】
つまり`x<y`と`x>=y`は反対の値になる、と書かれているように見えるのだが違うのだろうか。

```javascript
	null > 0;  // false
	null == 0; // false
	null < 0;  // false
	
	null >= 0; // true
	null <= 0; // true
```

Oh.