JavaScript 型変換
型変換は JavaScript で最もエラーが発生しやすい部分です。フォームに入力した数字?実は文字列だった!"5" + 3 が 8 ではなく "53" になる。初心者は誰もがこのような「驚き」に遭遇します。
📖 まとめ
型変換が重要な理由
HTML フォームの input 値は type="number" でも常に文字列です。算術演算を行う前に型を変換しないと、結果は予測不能になります。
HTML
<div id="demo"></div>
<script>
const input = "42";
const result = input + 8;
document.getElementById("demo").textContent = 'input + 8 = ' + result + '(型: ' + typeof result + ')';
</script>
明示的な変換:String()、Number()、Boolean()
変換関数を明示的に呼び出すことで意図が明確になります。推奨されるアプローチです。
| 変換 | 構文 | 例 |
|---|---|---|
| 文字列へ | String(value) |
String(123) → "123" |
| 数値へ | Number(value) |
Number("42") → 42 |
| 真偽値へ | Boolean(value) |
Boolean(0) → false |
暗黙の変換:+ による連結と == による比較
JavaScript エンジンは自動的な型変換を行いますが、しばしば予期しない結果を生みます。
+演算子:どちらかの側が文字列なら、もう片方も文字列に変換されて連結される==比較:比較前に型を変換する。複雑なルールで覚えにくい
HTML
<script>
console.log("5" + 3); // "53"(数値 3 が文字列に変換)
console.log("5" - 3); // 2(文字列 "5" が数値に変換)
console.log("" == 0); // true(空文字列が 0 に変換)
console.log(null == undefined); // true
</script>
よくある落とし穴
面接や実際の開発で高頻度に遭遇する落とし穴です。
HTML
<script>
console.log("5" + 3); // "53" + は文字列を見ると連結する
console.log("5" - 3); // 2 - は引き算のみ。文字列を数値に変換
console.log("5" * 3); // 15 * も数値に変換
console.log(true + 1); // 2 true は 1 に変換
console.log(false + 1); // 1 false は 0 に変換
console.log("" + 0); // "0" 数値が文字列に変換
console.log("" == 0); // true 両側が数値に変換
console.log(null == 0); // false null は undefined とのみ緩い等価
console.log("0" == false); // true 両方とも 0 に変換
</script>
このルールを覚えてください:+ は文字列に出会うと連結し、他のすべての算術演算子は文字列を数値に変換します。
Number() 変換ルール
| 入力 | 結果 |
|---|---|
Number("42") |
42 |
Number("3.14") |
3.14 |
Number("") |
0(空文字列 → 0。古典的な落とし穴) |
Number(" ") |
0(空白のみの文字列 → 0) |
Number("42px") |
NaN(数値以外の文字を含む → NaN) |
Number(true) |
1 |
Number(false) |
0 |
Number(null) |
0 |
Number(undefined) |
NaN |
空文字列が NaN ではなく 0 に変換されます。この設計で多くの開発者がつまずきました。
Boolean() 変換ルール(falsy 値)
以下の6つの値は false に変換され、falsy 値と呼ばれます。それ以外はすべて true です。
HTML
<script>
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
</script>
注意:Boolean("0") は true ですし、Boolean([]) も true です。空でない文字列と空でない配列は truthy です。
=== 厳密等価と == 緩い等価
| 演算子 | 動作 | 推奨 |
|---|---|---|
=== |
型が異なれば false を返す | 常に使う |
== |
比較前に型を変換 | 避ける |
HTML
<script>
console.log(5 === "5"); // false(型が異なる)
console.log(5 == "5"); // true(先に文字列が数値に変換)
console.log(null === undefined); // false
console.log(null == undefined); // true
</script>
鉄則:常に === を使い、== は使わないこと。 何をしているか正確に理解している場合を除き(例:value == null は null と undefined の両方にマッチ)。
例:型変換の比較
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>型変換</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 700px; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
th { background: #4a90d9; color: #fff; }
.surprise { color: #d9534f; font-weight: bold; }
.normal { color: #5cb85c; }
tr:nth-child(even) { background: #f9f9f9; }
</style>
</head>
<body>
<h2>暗黙の変換結果を一目で確認</h2>
<div id="output"></div>
<script>
const tests = [
['"5" + 3', "5" + 3, '"53"', "文字列連結", true],
['"5" - 3', "5" - 3, "2", "文字列を数値に変換", false],
['"5" * 3', "5" * 3, "15", "文字列を数値に変換", false],
['true + 1', true + 1, "2", "true→1", false],
['false + 1', false + 1, "1", "false→0", false],
['"" + 0', "" + 0, '"0"', "数値を文字列に変換", true],
['"" == 0', "" == 0, "true", "両側が数値に変換", true],
['"0" == false', "0" == false, "true", "両方とも 0 に変換", true],
['null == 0', null == 0, "false", "null は 0 に等しくない", false],
['5 === "5"', 5 === "5", "false", "型が異なる", false],
];
document.getElementById("output").innerHTML = `
<table>
<tr><th>式</th><th>実際の結果</th><th>型</th><th>説明</th><th>直感に反する?</th></tr>
${tests.map(t => `<tr>
<td><code>${t[0]}</code></td>
<td class="${t[4] ? 'surprise' : 'normal'}">${t[2]}</td>
<td>${typeof t[1]}</td>
<td>${t[3]}</td>
<td>${t[4] ? "はい" : "いいえ"}</td>
</tr>`).join("")}
</table>
`;
</script>
</body>
</html>
例:Boolean() falsy 値クイックリファレンス
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>真偽値変換</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; margin: 16px 0; }
.card { padding: 16px; border-radius: 8px; text-align: center; font-weight: bold; }
.falsy { background: #fff0f0; border: 2px solid #d9534f; color: #d9534f; }
.truthy { background: #f0fff0; border: 2px solid #5cb85c; color: #5cb85c; }
.card code { display: block; font-size: 18px; margin-bottom: 4px; }
.card span { font-size: 13px; opacity: 0.8; }
.note { background: #fff8e1; padding: 12px; border-radius: 6px; border-left: 4px solid #f0ad4e; margin: 16px 0; }
</style>
</head>
<body>
<h2>Boolean() 変換結果</h2>
<div id="output"></div>
<script>
const values = [
[false, "false"],
[0, "0"],
["", '""'],
[null, "null"],
[undefined, "undefined"],
[NaN, "NaN"],
[true, "true"],
[1, "1"],
["0", '"0"'],
["false", '"false"'],
[[], "[]"],
[{}], "{}"
];
const cards = values.map(([val, label]) => {
const result = Boolean(val);
const display = label || String(val);
return `<div class="card ${result ? 'truthy' : 'falsy'}">
<code>${display}</code>
<span>${result}</span>
</div>`;
});
document.getElementById("output").innerHTML = `
<div class="grid">${cards.join("")}</div>
<div class="note">
<code>"0"</code> は truthy です!<code>[]</code> も truthy です!あの7つの falsy 値だけが false に変換されます。
</div>
`;
</script>
</body>
</html>
例:フォーム入力の計算
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>フォーム計算</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.calc { max-width: 400px; margin: 16px auto; padding: 20px; background: #f9f9f9; border-radius: 8px; }
.row { margin: 12px 0; }
label { display: inline-block; width: 80px; }
input { padding: 8px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; width: 120px; }
button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; margin-top: 8px; }
button:hover { background: #357abd; }
.result { margin-top: 16px; padding: 12px; border-radius: 6px; font-size: 18px; font-weight: bold; }
.right { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.wrong { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
</style>
</head>
<body>
<h2 style="text-align:center;">フォーム入力の計算(型変換の実践)</h2>
<div class="calc">
<div class="row">
<label>価格:</label>
<input type="number" id="price" value="12.5" />
</div>
<div class="row">
<label>数量:</label>
<input type="number" id="qty" value="3" />
</div>
<div style="text-align:center;">
<button id="calcWrong">変換なしで計算(間違い)</button>
<button id="calcRight" style="background:#5cb85c;">Number() で計算</button>
</div>
<div class="result" id="result"></div>
</div>
<script>
document.getElementById("calcWrong").addEventListener("click", function() {
const price = document.getElementById("price").value;
const qty = document.getElementById("qty").value;
const total = price * qty;
document.getElementById("result").className = "result wrong";
document.getElementById("result").innerHTML =
`変換なし: "${price}" * "${qty}" = ${total}<br>` +
`(${typeof price} * ${typeof qty} = ${typeof total})<br>` +
`* は暗黙的に変換するのでうまくいきますが、+ だと壊れます!`;
});
document.getElementById("calcRight").addEventListener("click", function() {
const price = Number(document.getElementById("price").value);
const qty = Number(document.getElementById("qty").value);
if (isNaN(price) || isNaN(qty)) {
document.getElementById("result").className = "result wrong";
document.getElementById("result").textContent = "無効な入力です!";
return;
}
const total = price * qty;
document.getElementById("result").className = "result right";
document.getElementById("result").innerHTML =
`明示的な変換: ${price} × ${qty} = ${total.toFixed(2)}<br>` +
`(${typeof price} × ${typeof qty} = ${typeof total})`;
});
</script>
</body>
</html>
例:=== と == の比較
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>厳密等価と緩い等価</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 600px; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: center; }
th { background: #4a90d9; color: #fff; }
.same { background: #d4edda; }
.diff { background: #f8d7da; }
.recommend { background: #fff8e1; padding: 16px; border-radius: 8px; border-left: 4px solid #f0ad4e; margin: 16px 0; max-width: 600px; }
</style>
</head>
<body>
<h2>=== 厳密等価 vs == 緩い等価</h2>
<div id="output"></div>
<script>
const comparisons = [
["5", 5],
[0, false],
[0, ""],
["0", false],
[null, undefined],
[null, 0],
[null, false],
[NaN, NaN],
];
const rows = comparisons.map(([a, b]) => {
const loose = a == b;
const strict = a === b;
const aStr = JSON.stringify(a);
const bStr = JSON.stringify(b);
return `<tr>
<td><code>${aStr}</code> vs <code>${bStr}</code></td>
<td class="${loose ? 'same' : 'diff'}">${loose}</td>
<td class="${strict ? 'same' : 'diff'}">${strict}</td>
</tr>`;
});
document.getElementById("output").innerHTML = `
<table>
<tr><th>比較</th><th>== 結果</th><th>=== 結果</th></tr>
${rows.join("")}
</table>
<div class="recommend">
<strong>鉄則</strong>:常に <code>===</code> を使ってください。<code>==</code> の変換ルールは複雑すぎます。<code>value == null</code>(null と undefined の両方にマッチする特殊なケース)を除き、使わないでください。
</div>
`;
</script>
</body>
</html>
❓ よくある質問
Q
"5" - 3 が 2 になってエラーにならないのはなぜですか?A
- 演算子は数値演算のみを行うため、JavaScript が自動的に文字列を数値に変換します。+ 演算子だけが曖昧で(加算と連結の両方がある)、文字列を見ると連結します。他のすべての演算子(-、*、/、%)は常に数値に変換します。Q
Number("") が NaN ではなく 0 を返すのはなぜですか?A これはレガシーな設計上の選択です。空文字列は「数値がない」と見なされ、0 に相当します。つまり、
Number(input.value) は空のフィールドで NaN ではなく 0 を返すため、下流のロジックを誤らせる可能性があります。空の値をチェックするには、先に input.value.trim() === "" を使ってください。Q
null == 0 が false なのはなぜですか?A
== 比較では、null は undefined とのみ変換されます。数値には変換されません。そのため、null == 0 は false ですが、null == undefined は true です。これは仕様で定義された特殊ケースです。📝 演習
- コードを実行せずに、これらの式の結果を予測してください:
"10" + 5、"10" - 5、"10" * "2"、true + true、Boolean("false")、"1" == 1、"1" === 1。その後、コードを書いて検証してください。 calculateBMI(weight, height)関数を書きましょう。パラメータはフォーム入力(文字列)から取得します。関数内部で型変換してから BMI を計算し、空または無効な入力を処理してください(エラーメッセージを返す)。strictCompare(a, b)関数を書きましょう。===のみを使って比較してください。型が異なる場合は"型が異なります。比較できません"を直接返し、型が一致する場合は比較結果を返してください。



