[JS] 42==trueがfalseになるわけ

JSを少しでも書いてる人は値を比較するときに必ず`===`を使えと教わることだと思います。`==`がいかにエンジニアの直感に背き、値を比較するかを書いてみようと思います。

まずは、以下のコードが何をプリントするか予想してみてください。

``` js
console.log(42 == true); // ?
console.log('42' == true); // ?
console.log(1 == true); // ?
console.log(0 == false); // ?
```

.  
.  
.

予想できましたか？結果はこうなります。

``` js
console.log(42 == true); // false
console.log('42' == true); // false
console.log(1 == true); // true
console.log(0 == false); // true
```

`42 == true`や`'42' == true`は`true`になると予想したのではないでしょうか。なぜなら、`42`も`'42'`も単体では[truthy](https://developer.mozilla.org/ja/docs/Glossary/Truthy)だからです。

その証拠に`if`で単体で比較すると`true`になります。

``` js
if (42) {
  console.log('42 is true!');
} else {
  console.log('42 is false!');
}
if ('42') {
  console.log('true');
} else {
  console.log('false');
}
```

これらはを実行するとどちらも`42 is true!`と`true`が出力されます。なので、型強制(coercion)が行われ`true == true`が実行されたと考えるのが自然です。

ところがそうなりません。これはスペックを読むと理解することができます。([Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-abstract-equality-comparison)の6と7より)

> x == y
If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
(日本語)
もし、xがBooleanなら、ToNumber(x) == yを返す
もし、yがBooleanなら、y == ToNumber(y)を返す

つまり、実際に起きていたのは「Boolean型じゃない値をBoolean型に変換し比較」ではなく、「Boolean型の値をNumber型に変換し比較」だったのです。  
Boolean型をNumber型に変換すると`true`が1になり、`false`が0になります。そうすると、`42 == 1`と`'42' == 1`の比較になり当然結果は`false`になります。

同じ理由で`1 == true`は`true`になり、`0 == false`も`true`になります。

なので、タイトルの「JSで42==trueがfalseになるわけ」は「==で比較される値のうち片方がBooleanの場合、**Booleanの値**をNumberに型強制して比較するようにSpecに書かれているから」が答えになります。

.
.
.

せっかくなので、もう少し`==`を見てみましょう。片方がオブジェクトでもう片方が`String`, `Number`, `Symbol`の場合、オブジェクトを[プリミティブ型](https://developer.mozilla.org/ja/docs/Glossary/Primitive)に変換して比較することが指定されています。([7.2.14 Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-abstract-equality-comparison)の8と9より)

> x == y
If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
(日本語訳)
もし、xがString, Number, Symbolならx == ToPrimitive(y)を返す
もし、yがString, Number, SymbolならToPrimitive(x) == yを返す

ここで使われる`ToPrimitive`はJSエンジン内部で使われる処理のことで、オブジェクトのプロトタイプチェーンには定義されていません。ここでは`valueOf()`, `toString()`を順番に呼び、返ってきた値がプリミティブならその値を返す、プリミティブにならなかったら`TypeErroe`を返すという処理になっています。([7.1.1 ToPrimitive](https://www.ecma-international.org/ecma-262/9.0/index.html#sec-toprimitive))

これを使ってみるとこのような面白い結果になります。

``` js
console.log(1 == [1]); // true
console.log(123 == [123]); // true
console.log(true == [1]); // true
console.log(false == []); // true
```

`1 == [1]`は`[1].valueOf()`が`[1]`を返すので、`[1].toString()`を呼びます。そこでプリミティブの`"1"`が返ってくるため、`1 == "1"`の比較結果になります。型強制(coercion)が起こり`true`になります。

`123 == [123]`も前のサンプルと同様`123 == "123"`の比較になります。

`true == [1]`はトリッキーです。`true`があるため、まずここを処理しないといけません。`true`はNumber型に変換され、`1 == [1]`になります。これは最初のサンプルと同じなため、同じステップを踏み`true`になります。

`false == []`もいくつかのステップがあります。まず、`false`が`0`になり`0 == []`になります。`[].valueOf()`は`[]`のため、`[].valueOf()`の戻り値が使われます。`0 == ""`の比較です。`""`がNumber型に変換され`0`になり`0 == 0`は`true`です。空文字が0になるのは直感的ではないですが、そうなっています。

このように予期しない結果になるため、比較するときは必ず`===`を使いましょう。

(参考)
[Abstract Equality - You Dont Know JS](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch4.md#abstract-equality)