GitBookjs-primer暗黙的な型変換この章では、明示的な型変換と暗黙的な型変換について学んでいきます。等価演算子(==)ではなく厳密等価演算子(===)の利用を推奨していました。これは厳密等価演算子(===)が暗黙的な型変換を行わずに、値同士を比較できるためです。厳密等価演算子(===)では異なるデータ型を比較した場合に、その比較結果は必ずfalseとなります。次のコードは、数値の1と文字列の"1"という異なるデータ型を比較しているので、結果はfalseとなります。falseしかし、等価演算子(==)では異なるデータ型を比較した場合に、同じ型となるように暗黙的な型変換をしてから比較します。次のコードでは、数値の1と文字列の"1"の比較結果がtrueとなっています。これは、等価演算子(==)は右辺の文字列"1"を数値の1へと暗黙的な型変換してから、比較を行うためです。trueこのように、暗黙的な型変換によって意図しない結果となるため、比較には厳密等価演算子(===)を使うべきです。別の暗黙的な型変換の例として、数値と真偽値の加算を見てみましょう。多くの言語では、数値と真偽値の加算のような異なるデータ型同士の加算はエラーとなります。JavaScriptでは暗黙的な型変換が行われてから加算されるため、エラーなく処理されます。次のコードでは、真偽値のtrueが数値の1へと暗黙的に変換されてから加算処理が行われます。2JavaScriptでは、エラーが発生するのではなく、暗黙的な型変換が行われてしまうケースが多くあります。暗黙的に変換が行われた場合、プログラムは例外を投げずに処理が進むため、バグの発見が難しくなります。このように、暗黙的な型変換はできる限り避けるべき挙動です。この章では、次のことについて学んでいきます。明示的な変換だけでは解決しないこと暗黙的な型変換とは暗黙的な型変換とは次のことを言います。ある処理において、その処理過程で行われる明示的ではない型変換のこと暗黙的な型変換は、演算子による演算や関数の処理過程で行われます。ここでは、演算子における暗黙的な型変換を中心に見ていきます。等価演算子の暗黙的な型変換もっとも有名な暗黙的な型変換は、先ほども出てきた等価演算子(==)です。等価演算子は、オペランド同士が同じ型となるように暗黙的な型変換をしてから、比較します。次のように等価演算子(==)による比較は、驚くような結果を作り出します。trueこのほかにも等価演算子による予想できない結果は、比較する値と型の組み合わせの数だけあります。そのため、等価演算子の比較結果がどうなるかを覚えるのは現実的でありません。等価演算子の暗黙的な型変換を避ける簡単な方法があります。それは、常に厳密等価演算子(===)を使うことです。値を比較する際は、常に厳密等価演算子を使うことで、暗黙的な型変換をせずに値を比較できます。false厳密等価演算子(===)を使うことで、意図しない比較結果を避けることができます。そのため、比較には等価演算子(==)ではなく厳密等価演算子(===)を使うことを推奨します。他の演算子についても、具体的な例を見てみましょう。次のコードでは、数値の1と文字列の"2"をプラス演算子で処理しています。プラス演算子(+)は、数値の加算と文字列の結合を両方実行できるように多重定義されています。このケースでは、JavaScriptは文字列の結合を優先する仕様となっています。そのため、数値の1を文字列の"1"へ暗黙的に変換してから、文字列結合します。"12"もうひとつ、数値と文字列での暗黙的な型変換を見てみましょう。次のコードでは、数値の1から文字列の"2"を減算しています。JavaScriptには、文字列に対するマイナス演算子(-)の定義はありません。そのため、マイナス演算子の対象となる数値への暗黙的な型変換が行われます。これにより、文字列の"2"を数値の2へ暗黙的に変換してから、減算します。-12つの値までは、まだ結果の型を予想できます。3つ以上の値を扱う場合に結果を予測できなくなります。演算する順番によっても結果が異なります。自動的に変換されることを暗黙的な型変換と呼んでいます。暗黙的な型変換では、結果の値の型はオペランドの型に依存しています。明示的な型変換をする必要があります。明示的な型変換プリミティブ型へ明示的な型変換をする方法を見ていきます。真偽値JavaScriptではBooleanコンストラクタ関数を使うことで、任意の値をtrueまたはfalseの真偽値に変換できます。falseJavaScriptでは、どの値がtrueでどの値がfalseになるかは、次のルールによって決まります。falsyな値はfalseになるfalsyでない値はtrueになるfalsyな値とは次の6種類の値のことを言います。falseundefinednull0NaN""(空文字列)この変換ルールはif文の条件式の評価と同様です。次のようにif文に対して、真偽値以外の値を渡したときに、真偽値へと暗黙的に変換されてから判定されます。}真偽値については、暗黙的な型変換のルールが少ないため、明示的に変換せずに扱われることも多いです。正確な判定をして真偽値を得るには、次のように厳密等価演算子(===)を使って比較します。数値から文字列へ明示的に変換する場合は、Stringコンストラクタ関数を使います。"1"Stringコンストラクタ関数は、数値以外にもいろいろな値を文字列へと変換できます。{}"上記の結果からもわかるようにStringコンストラクタ関数での明示的な変換は、万能な方法ではありません。undefined、null、シンボルのプリミティブ型の値に対して変換は見た目どおりの文字列を得ることができます。オブジェクトに対しては、あまり意味のある文字列を返しません。オブジェクトに対してはStringコンストラクタ関数より適切な方法があるためです。配列にはjoinメソッド、オブジェクトにはJSON.stringifyメソッドなど、より適切な方法があります。そのため、Stringコンストラクタ関数での変換は、あくまでプリミティブ型に対してのみに留めるべきです。文字列プラス演算子を文字列に利用した場合、文字列の結合を優先します。「片方が文字列なら、もう片方のオペランドとは関係なく、結果は文字列となるのでは?」と考えるかもしれません。文字列となる?しかし、ES2015で追加されたプリミティブ型であるシンボルは暗黙的に型変換できません。文字列結合演算子をシンボルに対して利用すると例外を投げるようになっています。そのため、片方が文字列であるからといってプラス演算子の結果は必ず文字列になるとは限らないことがわかります。次のコードでは、シンボルを文字列結合演算子(+)で文字列に変換できないというTypeErrorが発生しています。stringこの問題もStringコンストラクタ関数を使うことで、シンボルを明示的に文字列化することで解決できます。文字列から数値に変換する典型的なケースとしては、ユーザー入力として数字を受け取ることがあげられます。ユーザー入力は文字列でしか受け取ることができないため、それを数値に変換してから利用する必要があります。文字列から数値へ明示的に変換するにはNumberコンストラクタ関数が利用できます。入力された文字列を数値に変換したものまた、文字列から数字を取り出して変換する関数としてNumber.parseInt、Number.parseFloatも利用できます。Number.parseIntは文字列から整数を取り出し、Number.parseFloatは文字列から浮動小数点数を取り出すことができます。基数)の第二引数には基数を指定します。文字列をパースして10進数として数値を取り出したい場合は、第二引数に基数として10を指定します。10.5しかし、ユーザーが数字を入力するとは限りません。Number)を返します。NaNそのため、任意の値から数値へ変換した場合には、NaNになってしまった場合の処理を書く必要があります。変換した結果がNaNであるかはNumber.isNaN(x)メソッドで判定できます。NumberだけどNumber型ここで、数値への型変換でたびたび現れるNaNという値について詳しく見ていきます。Numberの略称で、特殊な性質を持つNumber型のデータです。JavaScriptだけの性質ではありません。NaNという値を作る方法は簡単で、Number型と互換性のない性質のデータをNumber型へ変換した結果はNaNとなります。オブジェクトは数値とは互換性のないデータです。そのため、オブジェクトを明示的に変換したとしても結果はNaNになります。NaNまた、NaNは何と演算しても結果はNaNになる特殊な値です。次のように、計算の途中で値がNaNになると、最終的な結果もNaNとなります。NaNNaNはNumber型の一種であるという名前と矛盾したデータに見えます。"number"NaNしか持っていない特殊な性質として、自分自身と一致しないというものがあります。この特徴を利用することで、ある値がNaNであるかを判定できます。true同様の処理をする方法としてNumber.isNaN(x)メソッドがあります。実際に値がNaNかを判定する際には、Number.isNaN(x)メソッドを利用するとよいでしょう。trueNaNは暗黙的な型変換の中でももっとも避けたい値となります。理由として、先ほど紹介したようにNaNは何と演算しても結果がNaNとなってしまうためです。これにより、計算していた値がどこでNaNとなったのかがわかりにくく、デバッグが難しくなります。次のsum関数は可変長引数(任意の個数の引数)を受け取り、その合計値を返します。z)と呼び出したときの結果がNaNになってしまいました。これは、引数の中にundefined(未定義の値)が含まれているためです。z);は次のように呼ばれていたのと同じ結果になります。undefinedに数値を加算すると結果はNaNとなります。NaNこれは、sum関数において引数を明示的にNumber型へ変換したとしても回避できません。次のように明示的な型変換をしても解決できないことがわかります。NaNこの意図しないNaNへの変換を避ける方法として、大きく分けて2つの方法があります。どちらも行うことがより安全なコードにつながります。そのためには、sum関数が数値のみを受けつけるということを明示する必要があります。引数に数値以外の値がある場合は例外を投げるという処理を追加するといった形です。JavaScriptではコメントで引数の型を記述する書式としてJSDocが有名です。(throw文については「例外処理」の章で解説します)。この2つを利用してsum関数の前提条件を詳細に実装したものは次のようになります。数値を合計した値を返します。1つ以上の数値と共に呼び出す必要があります。エラーとなったときに呼ばれる側と呼び出し側でどちらに問題があるのかが明確になります。この場合は、sum関数へundefinedな値を渡している呼び出し側に問題があります。JavaScriptは、型エラーに対して暗黙的な型変換をしてしまうなど、驚くほど曖昧さを許容しています。そのため、大きなアプリケーションを書く場合は、このような検出しにくいバグを見つけられるように書くことが重要です。明示的な変換でも解決しないこと先ほどの例からもわかるように、あらゆるケースが明示的な変換で解決できるわけではありません。Number型と互換性がない値を数値にしても、NaNとなってしまいます。一度、NaNになってしまうとNumber.isNaN(x)で判定して処理を終えるしかありません。JavaScriptの型変換は基本的に情報が減る方向へしか変換できません。そのため、明示的な変換をする前に、まず変換がそもそも必要なのかを考える必要があります。空文字列かどうかを判定するたとえば、文字列が空文字列なのかを判定したい場合を考えてみましょう。""(空文字列)はfalsyな値であるため、明示的にBooleanコンストラクタ関数で真偽値へ変換できます。falsyな値は空文字列以外にもあるため、明示的に変換したからといって空文字列だけを判定できるわけではありません。次のコードでは、明示的な型変換をしていますが、0も空文字列となってしまい意図しない挙動になっています。trueほとんどのケースにおいて、真偽値を得るには型変換ではなく別の方法が存在します。この場合、空文字列とは「String型で文字長が0の値」であると定義することで、isEmptyString関数をもっと正確に書くことができます。次のように実装することで、値が空文字列であるかを正しく判定できるようになりました。falseBooleanを使った型変換は、楽をするための型変換であり、正確に真偽値を得るための方法ではありません。そのため、型変換をする前にまず別の方法で解決できないかを考えることも大切です。まとめこの章では暗黙的な型変換と明示的な型変換について学びました。暗黙的な型変換は意図しない結果となりやすいため避ける比較には等価演算子(==)ではなく、厳密等価演算子(===)を利用する演算子による暗黙的な型変換より、明示的な型変換を行う関数を利用する真偽値を得るには、明示的な型変換以外の方法もある。