では、上で紹介した 実際のバグ事例5つ を、
「バグ版 → 修正版 → 解説」 のセットで、
実際に動かせる具体的な JavaScript コード で見ていきましょう。
例1:比較のバグ
// ❌ バグ版:結果は "different!"
const s1 = "apple";
const s2 = new String("apple");
if (s1 === s2) {
console.log("same!");
} else {
console.log("different!");
}
// ✅ 修正版:valueOf() を使うか、String() で変換
if (s1 === s2.valueOf()) {
console.log("same! (fixed)");
}
if (String(s1) === String(s2)) {
console.log("same! (also fine)");
}
JavaScript🧠 解説:=== は型も比較するため、string と object は別物。
比較時は プリミティブ化 (.valueOf() または String()) するのが安全。
例2:真偽値チェックのバグ
// ❌ バグ版:表示される(空文字なのに)
const emptyObj = new String("");
if (emptyObj) {
console.log("❌ 空文字なのに真と判定される");
}
// ✅ 修正版
if (emptyObj.valueOf()) {
console.log("✅ 空でない文字列");
} else {
console.log("🟢 空文字と正しく判定できた");
}
// ✅ 普通はこれで十分
const emptyStr = "";
if (emptyStr) console.log("真"); // 表示されない(空文字は falsy)
JavaScript🧠 解説:new String("") は「空文字を包んだオブジェクト」なので、if (emptyObj) は truthy(=常に真)になります。
プリミティブ文字列ならこの誤動作は起きません。
例3:配列検索が失敗
// ❌ バグ版:false
const fruits = ["apple", "banana", "cherry"];
const key = new String("banana");
console.log(fruits.includes(key)); // false
// ✅ 修正版1:プリミティブ化して比較
console.log(fruits.includes(key.valueOf())); // true
// ✅ 修正版2:最初からプリミティブにする
const key2 = "banana";
console.log(fruits.includes(key2)); // true
JavaScript🧠 解説:Array.prototype.includes は「===」で比較するため、"banana" と new String("banana") は一致しません。
例4:Map のキーが別扱い
// ❌ バグ版:同じ文字列でも別のキーとして扱われる
const map = new Map();
map.set("user", "Alice");
map.set(new String("user"), "Bob");
console.log(map.get("user")); // "Alice"
console.log(map.get(new String("user"))); // "Bob"
// ✅ 修正版:常にプリミティブ文字列をキーに
const safeMap = new Map();
safeMap.set("user", "Alice");
console.log(safeMap.get("user")); // "Alice"
JavaScript🧠 解説:new String("user") はオブジェクトなので、
Mapでは別の参照扱いになり、同じキーとして見なされません。
例5:JSON変換の落とし穴
// ❌ バグ版:StringオブジェクトはJSON化されると {}
const data1 = { msg: new String("Hello") };
console.log(JSON.stringify(data1)); // {"msg":{}}
// ✅ 修正版:プリミティブにする
const data2 = { msg: "Hello" };
console.log(JSON.stringify(data2)); // {"msg":"Hello"}
// ✅ 修正版2:valueOf() で明示的に変換
const data3 = { msg: (new String("Hello")).valueOf() };
console.log(JSON.stringify(data3)); // {"msg":"Hello"}
JavaScript🧠 解説:
JSON.stringify は「オブジェクトのプロパティ」を展開するため、Stringオブジェクト の中身の文字列は無視されて {} になります。
追加:安全な比較ユーティリティ
// どんな文字列でも(プリミティブ or Stringオブジェクト)安全に比較
function safeStringEqual(a, b) {
return String(a) === String(b);
}
console.log(safeStringEqual("abc", new String("abc"))); // true
console.log(safeStringEqual(new String("x"), new String("x"))); // true
console.log(safeStringEqual("", new String(""))); // true
JavaScriptコードレビューでの指摘テンプレート
⚠️ レビューコメント例:
new String()を使っていますが、これはプリミティブ"..."とは型が異なり、
比較や真偽値判定で誤動作を引き起こす恐れがあります。
→String()関数(newなし)またはリテラル"..."を使用してください。
まとめ
| バグの種類 | 原因 | 修正法 |
|---|---|---|
| 比較で false | 型が違う | .valueOf() / String() |
| 空文字 truthy | オブジェクト扱い | プリミティブ使用 |
| 配列検索失敗 | === 比較 | .valueOf() |
| Mapキー別扱い | 参照の違い | プリミティブ使用 |
JSONが {} になる | toJSONなし | .valueOf() or プリミティブ |

