[JS] 42==trueがfalseになるわけ
[JS] 42==trueがfalseになるわけ:
JSを少しでも書いてる人は値を比較するときに必ず
まずは、以下のコードが何をプリントするか予想してみてください。
.
.
.
予想できましたか?結果はこうなります。
その証拠に
これらはを実行するとどちらも
ところがそうなりません。これはスペックを読むと理解することができます。(Abstract Equality Comparisonの6と7より)
Boolean型をNumber型に変換すると
同じ理由で
なので、タイトルの「JSで42==trueがfalseになるわけ」は「==で比較される値のうち片方がBooleanの場合、Booleanの値をNumberに型強制して比較するようにSpecに書かれているから」が答えになります。
.
.
.
せっかくなので、もう少し
これを使ってみるとこのような面白い結果になります。
このように予期しない結果になるため、比較するときは必ず
(参考)
Abstract Equality - You Dont Know JS
JSを少しでも書いてる人は値を比較するときに必ず
===
を使えと教わることだと思います。==
がいかにエンジニアの直感に背き、値を比較するかを書いてみようと思います。まずは、以下のコードが何をプリントするか予想してみてください。
console.log(42 == true); // ? console.log('42' == true); // ? console.log(1 == true); // ? console.log(0 == false); // ?
.
.
予想できましたか?結果はこうなります。
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だからです。その証拠に
if
で単体で比較するとtrue
になります。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の6と7より)
x == yつまり、実際に起きていたのは「Boolean型じゃない値をBoolean型に変換し比較」ではなく、「Boolean型の値をNumber型に変換し比較」だったのです。
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型を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
の場合、オブジェクトをプリミティブ型に変換して比較することが指定されています。(7.2.14 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)これを使ってみるとこのような面白い結果になります。
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
コメント
コメントを投稿