JavaScript初心者の勘違い
JavaScriptを学ぶ中で、「なんとなくこうだろう」と思っていたことについて、仕様や実際の挙動をもとに整理し基礎を固めていくシリーズです。
「自分も同じ勘違いをしていた!」という方の参考になれば嬉しいです。
はじめに
前回、「"false" == falseがなぜfalseになるのか」について、==での評価方法と
Number型での変換を順に追いながら学習しました。
(前回記事はこちら)
今回は、ChatGPTに出題してもらった型変換関連の例題について、学んだ知識と
仕様の記述を確認ながら解説を作るように学習していこうと思います。
問題はこちらです。
[] == false
[] == ![]
[] == false について
考え方
(1) Boolean→Numberへの型変換
参考:ECMASctipt 型変換の仕様
ECMAScriptの仕様で、以下記載があります。
9. If x is a Boolean, return ! IsLooselyEqual(! ToNumber(x), y).
10. If y is a Boolean, return ! IsLooselyEqual(x, ! ToNumber(y)).
x == yという式があったとき、
- xがBooleanの場合はToNumber(x)を行う(※1)
- yがBooleanの場合はToNumber(y)を行う(※1)
と書いてあります。
今回は右辺がBoolean型なので、2つめの記述に当てはまります。
※1 ToNumberはECMAScript仕様で定義されている内部的な型変換処理です。
JavaScriptから直接呼び出すことはできませんが、Number()と同じ変換結果に
なります。
そのため、Boolean型のfalseをNumber型に変換し、[] == 0となります。
(2) Object→Primitiveへの型変換
こちらも、ECMAScriptの仕様にて以下記載があります。
11. If x is either a String, a Number, a BigInt, or a Symbol and y is an Object,
return ! IsLooselyEqual(x, ? ToPrimitive(y)).
12. If x is an Object and y is either a String, a Number, a BigInt, or a Symbol,
return ! IsLooselyEqual(? ToPrimitive(x), y).
x == yという式があったとき、
- xがString・Number・BigInt・シンボルでyがObjectの場合は、ToPrimitive(y)を行う
- xがObjectでyがString・Number・BigInt・シンボルの場合は、ToPrimitive(x)を行う
上記をまとめると、
String・Number・BigInt・シンボルのいずれかとObjectを比較する場合、
ObjectをToPrimitive()でPrimitive型に変換してください
となります。
ここでもToPrimitive()は仕様上の内部的な型変換処理のことを指します。
今回は[] == 0でObjectとNumberの比較になるため、
左辺の[]をToPrimitive([])によりPrimitive型に変換し、最終的には""(空文字)
になります。
そのため、"" == 0の比較になります。
※ToPrimitive([])が""となる内部的な仕組みについては、また調査して別記事にまとめたいと
思います。
(3) String→Numberへの型変換
"" == 0でStringとNumberの比較になるため、
左辺の""をStringからNumberに変換し、0となります。
0 == 0となり、結果はtrueになります。
[] == ![] について
考え方
(1) 右辺:![]を評価する
評価の優先順位は!>==のため、先に右辺![]を評価します。
!は対象の真偽値を反転させる演算子です。
対象が真偽値でない場合は内部的に真偽値に変換してから反転します。
Boolean()では、引数がTruthyであればtrueを、Falsyであればfalseに変換します。
[]はTruthyな値のため、[]→trueとなり、
右辺!true→falseとなります。
結果、[] == falseとなります。
(2) 右辺:Boolean→Numberへの変換
[] == falseで、ObjectとBooleanの比較なので、右辺をBoolean→Numberに変換し
0となります。
これにより、[] == 0となります。
(3) 左辺:Object→Primitiveへの型変換
左辺[]をToPrimitive([])によりPrimitive型""(空文字)に変換します。
"" == 0となります。
(4) 左辺:String→Numberへの変換
StringとNumberの比較になるのでString→Numberに変換します。
Number()では""は0となるので
結果として0 == 0となりtrueとなります。
型変換の処理順について
ここで、私は型変換の処理順について疑問に思いました。
(例えば、結果は同じだけど上記の(2)と(3)を入れ替えて考えてはいけないのか)
仕様では==での評価はIsLooselyEqualという操作として定義されています。
この中では、Ifという言葉を使って順番に記述してあります。
ということは、現在の状態に合致する処理を上から順に見ていく必要があります。
仕様では以下のように記述されているため、「Boolean→Numberへの変換」を
「Object→Primitiveへの型変換」より先に処理します。
9. If x is a Boolean, return ! IsLooselyEqual(! ToNumber(x), y).
10. If y is a Boolean, return ! IsLooselyEqual(x, ! ToNumber(y)).
11. If x is either a String, a Number, a BigInt, or a Symbol and y is an Object, return ! IsLooselyEqual(x, ? ToPrimitive(y)).
12. If x is an Object and y is either a String, a Number, a BigInt, or a Symbol, return ! IsLooselyEqual(? ToPrimitive(x), y).
まとめ
今回は、Object型が含まれる==の結果について、仕様を確認しながら
自分用に解説を作るため下記について学習しました。
- Object→Primitive の変換方法
- 評価の優先順位
- 型変換の処理順
おわりに
内部的処理まで理解しようとすることで、疑問がさらなる疑問を呼び、
仕様書をしっかり確認するところまでたどりつけました。
普段の業務や学習の中では、「この場合、結果はこうなる」「これはこういう仕様」
というところで終わってしまい、仕様書まで読み込んで確認することって
なかなかできていなかったなあと思います。
疑問に思ったことはメモしておき、時間のあるときに深堀りして
基礎を固めることを大切にしていきたいと思います。
Object→Primitiveの型変換について調査していたところ、hintという値を渡すことや
それによる内部処理がいろいろある という情報を見つけました。
ToPrimitive([])が""となることを例に挙げて別記事でまとめてみたいです。
次回
またまた型変換問題でChatGPTに出題してもらった例題を解説を作って学習していきます。
null、undefinedなどなんとなくは知っているけど改めて基礎に立ち返りたいと思います。
次回で型変換に関係する勘違いシリーズはいったん完結する予定です。
参考