JavaScript | Stringオブジェクト

JavaScript JavaScript
スポンサーリンク

では、上で紹介した 実際のバグ事例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

🧠 解説
=== は型も比較するため、stringobject は別物。
比較時は プリミティブ化 (.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 プリミティブ
タイトルとURLをコピーしました