JavaScriptの==の中身を知る

# 概要
この記事は、[We Are JavaScripters! @6th](https://wajs.connpass.com/event/54667/)での発表の補完資料です。

JavaScriptでは同じであるかという条件の真偽値を導きだすために用意された演算子が二つあります。
**`==`**と、**`===`**です。
[ECMAScript](https://www.ecma-international.org/ecma-262/5.1/)を参考にから、それぞれどのような流れで真偽値を導き出しているのか、調べてみました。

# 曖昧な比較と厳密な比較
`==`は曖昧な比較、`===`は厳密な比較を行います。
ですので、下記のようにNumberである1とStringである'1'を比較すると、`==`ではtrueとなるのに対して、`===`ではfalseとなるように挙動に違いが発生します。

```js
// 曖昧な比較では型を比較しない
> 1 == '1'
true
// 厳密な比較では型も比較する
> 1 === '1'
false
```

# `===`の処理の流れ
[11.9.4 The Strict Equals Operator ( === ) - ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.4)
[11.9.6 The Strict Equality Comparison Algorithm - ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.6)
早速上記を参考に処理の流れを追っていきます。
最初は処理の流れとしては単純な`===`です。

まず、型の比較を行います。
この時点で違っていた場合はfalseを返します。

次に、値の比較を行います。
プリミティブ型[^1]はそのまま比較、参照型(オブジェクト)は参照を比較します。
nullとundefinedはプリミティブ型と同じ比較を行います。
NaN同士は同じ値とみなされない性質上、falseになります。
また、+0と-0の比較は同一とみなされ、trueになります。

# ==の処理の流れ
[11.9.1 The Equals Operator ( == ) - ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.1)
[11.9.3 The Abstract Equality Comparison Algorithm - ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3)
次に上記を参考に`==`の流れを追ってみました。

まず、型が同じであれば値の比較を行います。
値が同一であればtrue、違っていた場合はfalseを返す。

**プリミティブ型の変換**
片方の値がnullでもう片方の値がundefinedの場合、等しいと判定します。
片方の値が数値でもう片方が文字列の場合、文字列を数値に変換してから、再度`==`の曖昧な比較をします。
片方がtrueの場合、trueを1に、falseの場合は0に変換してから、再度==の曖昧な比較をします。

**オブジェクトの変換**
[9.1 ToPrimitive - ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/#sec-9.1)
[8.12.8 \[\[DefaultValue\]\] \(hint\) - ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8)
片方がオブジェクトでもう片方が数値または文字列の場合、オブジェクトをプリミティブ型に変換してから比較を行います。
比較のときには、基本的にオブジェクトは数値に変換するときと同じ流れをとります。
まず、オブジェクトのvalueOfメソッドを呼び出し、プリミティブ型が返るか確認します。

```js
> [1, 2, 3].valueOf()
[ 1, 2, 3 ]

> {a: 1, b: 2, c: 3}.valueOf()
{ a: 1, b: 2, c: 3 }
```

そのときにプリミティブ型が返る場合、その値を使用して再度`==`での比較を最初から行います。
プリミティブ型が得られない場合、オブジェクトのtoStringメソッドを呼び出し、プリミティブ型が返るか確認します。

```js
> [1, 2, 3].toString()
'1,2,3'

> ({a: 1, b: 2, c: 3}.toString())
'[object Object]'
```
そのときにプリミティブ型が返る場合、その値を使用して再度`==`の比較を行います。
上記に当てはまるものがない場合、falseを返します。

Dateなどの例外はありますが、基本的にはこの流れをとります。

# `==`を使用した時に起きうる問題
**バッドケース**

```js
// 厳密に数値を取りたい場合に紛らわしい
> '01' == 1
true

// 空文字と数値の0が同じになってしまう
> '' == 0
true

// nullとundefinedは明確に意味が違うので区別すべき
> null == undefined
true

// そもそもこんなこと書かないけどtrueになる
>'[objet Object]' == {}
true
```

# 参考
[ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/)
[比較演算子 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)


[^1]: ここでは、Undefined、Null、Boolean、Number、Stringの型の値のこと　([4.3.2 primitive value - ECMAScript Language Specification - ECMA-262 Edition 5.1](https://www.ecma-international.org/ecma-262/5.1/#sec-4.3.2))