Q1console.log("5" + 1); を実行すると何が表示されますか?
型変換 — Number / String / parseInt と暗黙変換
入力欄から届いた文字列を数値に変える明示的な変換と、演算子が自動で型をそろえる暗黙の変換を扱います。typeof での型の確認、変換に失敗したときの NaN の見分け方まで押さえます。
入力欄に打ち込まれた数量や、外部から受け取ったデータの中の金額。こうした値は、画面に出ている見た目が同じ 1200 でも、JavaScript が文字列として持っているか数値として持っているかで計算の結果が変わります。
この記事では、値の型を自分で変える 明示的な型変換(Number などを呼んで型を変える書き方)と、演算子が自動で型をそろえる 暗黙の型変換(演算子が計算の前に型を合わせる動き)の 2 つを扱います。
今の型を確かめる — typeof
型をそろえる作業は、いま何型なのかが分かっていないと始まりません。合計金額がおかしい、比較が思った結果にならないといった不具合は、値が数値だと思い込んでいたところが実は文字列だった、という取り違えから起こります。
まず、値の型をその場で確認する方法を押さえます。
typeof(値の型名を文字列で返す演算子)を値の前に置くと、その値の型が分かります。typeof 1200 は "number"、typeof "1200" は "string" です。
返ってくるのは型そのものではなく 型名を表す文字列 なので、判定に使うときは typeof price === "number" のようにクォートで囲んだ文字列と比べます。
const orderId = "A-1042"; // 注文番号
console.log(typeof orderId); // string
console.log(typeof 500); // number
console.log(typeof true); // boolean
// 変数だけでなく、式の結果にも使える
console.log(typeof (500 + 100)); // number
console.log(typeof (500 > 100)); // boolean
console.log(typeof `注文番号 ${orderId}`); // string
暗黙の変換 — 足し算だけが文字列連結になる
型が違う値どうしを計算しても、JavaScript はエラーで止まりません。演算子が計算の前に型をそろえて、そのまま先へ進みます。
== が比較の前に型をそろえていたのと同じ動きが、算術演算子でも起きます。止まらないぶん、結果を見るまで取り違えに気付けません。
問題は、+ だけが他の演算子と違う判断をすることです。+ は、片方が文字列なら文字列連結を選びます。数値の側が文字列に変換され、"3" + 1 は "31" になります。
一方 - * / % には文字列どうしをつなぐ意味がないので、文字列のほうを数値に変換してから計算します。
合計欄に金額が横に並んで表示されるときは、足す前に変換されていないと考えられます。
+ だけが 文字列連結 を優先します。ほかの算術演算子は文字列のほうを数値に変換します。const inputQuantity = "3"; // 数量の入力欄から受け取った値 (文字列)
// 表示だけでは数値の 31 と文字列の "31" を見分けられないので、下の typeof で確かめる
console.log(inputQuantity + 1); // 31 文字列 "31" として連結される
console.log(inputQuantity - 1); // 2 数値に変換してから引く
console.log(inputQuantity * 2); // 6 掛け算も数値に変換される
console.log(inputQuantity / 3); // 1 割り算も数値に変換される
// + の結果だけ型が違うことを typeof で確かめられる
console.log(typeof (inputQuantity + 1)); // string
console.log(typeof (inputQuantity - 1)); // number
外から届いた値は文字列だと考える
入力欄や、外部から受け取ったデータの中の数字は、文字列として届くのが普通 です。1200 + 300 のつもりで書いた足し算が "1200300" のように横に並ぶときは、片方が文字列のままだったサインです。足す前に数値へ変換します。
明示的に型を変える — Number / String / Boolean
演算子任せの変換は、どちらの型に寄るかが演算子ごとに決まっているため、書いた本人の意図が残りません。計算に使う値は、計算する前に自分で型をそろえて おくと、結果を追いやすくなります。
JavaScript には型ごとに変換用の関数が用意されています。
Number(値) は数値へ、String(値) は文字列へ、Boolean(値) は真偽値へ変換した 新しい値を返します。元の変数は書き換わりません。
Boolean が返す結果は falsy の規則そのままで、"" や 0 は false、中身のある文字列は true です。なお Number("") は NaN ではなく 0 を返すので、未入力の欄をそのまま数値にすると 0 円として扱われます。
const inputQuantity = "3"; // 数量の入力欄から受け取った値
const shippingFee = 500; // 送料 (数値)
console.log(Number(inputQuantity)); // 3 文字列から数値へ
console.log(Number(inputQuantity) + 1); // 4 足し算も数値として動く
console.log(String(shippingFee)); // 500 数値から文字列へ
console.log(typeof String(shippingFee)); // string
console.log(Boolean("東京都")); // true 中身のある文字列
console.log(Boolean("")); // false 空文字列は falsy
console.log(Number("")); // 0 空文字列は 0 になる
// 変換しても元の変数は文字列のまま
console.log(typeof inputQuantity); // string
変換に失敗した値 — NaN と Number.isNaN
数値にできない文字列を Number に渡しても、JavaScript はエラーを出しません。代わりに NaN(数値として表せなかったことを表す値)を返し、そのまま次の計算へ進みます。
金額欄に全角文字やカンマが混ざっていただけで、合計が最後まで NaN のまま画面に出てしまうのはこのためです。変換したあとに、成功したかどうかを自分で確かめる必要があります。
ここで total === NaN と書いても判定できません。NaN は自分自身と比べても一致しない ためで、結果は必ず false です。
typeof NaN も "number" を返すので型でも見分けられません。判定には Number.isNaN(渡された値が NaN かどうかを返す関数)を使います。
const inputAmount = "1,200"; // カンマ付きで打ち込まれた金額
console.log(Number(inputAmount)); // NaN カンマがあると変換できない
console.log(Number(inputAmount) + 500); // NaN 混ざったあとの計算も NaN
// 比較でも型でも見分けられない
console.log(Number(inputAmount) === NaN); // false
console.log(typeof NaN); // number
// 判定は専用の関数を使う
console.log(Number.isNaN(Number(inputAmount))); // true
数字で始まる文字列を読む — parseInt と parseFloat
"240px" や "12.5%" のように、数字のうしろに単位が付いた文字列を扱う場面があります。Number は 文字列全体 を数値として読むため、単位が残っていると NaN になります。
先頭の数字だけを取り出したいときは別の関数を使います。
parseInt(先頭から整数として読める範囲を取り出す関数)は "240px" から 240 を取り出します。2 つ目の引数に渡す 10 は「ふだん使う 0〜9 の数え方で読む」という指定です。省略もできますが、書いておくと読む側に意図が伝わります。
小数点まで含めて読みたいときは parseFloat(先頭から小数として読める範囲を取り出す関数)を使います。どちらも先頭から数字として読める部分が無ければ NaN を返します。
Number は 文字列全体 を見ます。parseInt と parseFloat は 先頭から読める範囲 だけを取り出し、小数点まで読むかどうかが違います。const cardWidth = "240px"; // スタイル指定から読み取った幅
console.log(Number(cardWidth)); // NaN 全体を数値にできない
console.log(parseInt(cardWidth, 10)); // 240 先頭から読める分だけ
console.log(parseFloat("12.5%")); // 12.5 小数点まで読み取る
console.log(parseInt("12.5%", 10)); // 12 整数として読むので小数は落ちる
// 先頭が数字でなければ読み取れない
console.log(parseInt("約 240px", 10)); // NaN
先頭だけ読むので気付きにくい取りこぼしが出る
parseInt は 読めなくなった時点で止まります。カンマ区切りの "1,200" は 1 になり、"12.5" は 12 になります。どちらもエラーにならないので、金額のように全体を読み切りたい値には Number を使い、単位付きの文字列にだけ parseInt を使います。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2console.log(Number("240px")); の結果はどれですか?
Q3変換した結果が NaN かどうかを正しく判定できる書き方はどれですか?