代入演算子の誤用
条件式の中で誤って代入演算子=を使用すると、意図しない動作を引き起こします。比較を行う際は、等価比較の==または厳密な等価比較の===を使うべきです。
次のコードでは、変数xが10に代入され、その結果として真(true)が返されます。これは条件判定ではなく代入操作であるため、バグの原因になります。
let x = 0;
if (x = 10) {
console.log("実行されます"); // 注意:これは条件ではなく代入
}
正しくは以下のように比較演算子を使用します。
let x = 0;
if (x === 10) {
console.log("ここは実行されません");
}
比較演算子における型の扱い
抽象的な比較==では型変換が行われるため、予期しない一致が発生することがあります。
let a = 5;
let b = "5";
console.log(a == b); // true(文字列と数値が等しいと判定)
console.log(a === b); // false(型が異なるため)
特にswitch文では、各caseとの比較に厳密等価(===)が使用されるため、データ型の整合性が重要です。
let value = 10;
switch(value) {
case "10":
console.log("ここは実行されない");
break;
case 10:
console.log("ここが実行される");
break;
}
加算と文字列連結の混同
+演算子は数値の加算にも、文字列の連結にも使われます。片方が文字列の場合、他方も自動的に文字列に変換されます。
let result1 = 20 + 30; // 50(数値)
let result2 = 20 + "30"; // "2030"(文字列)
let result3 = 20 + 30 + "5"; // "505"(左から評価)
let result4 = "5" + 20 + 30; // "52030"
この振る舞いは、演算順序とデータ型によって結果が変わるため注意が必要です。
浮動小数点数の精度問題
JavaScriptはIEEE 754に従って64ビット浮動小数点数を使用しており、一部の小数値は正確に表現できません。
let a = 0.1;
let b = 0.2;
let sum = a + b;
console.log(sum); // 0.30000000000000004
console.log(sum === 0.3); // false
これを避けるには、整数化して計算後に戻す方法があります。
let fixedSum = (a * 10 + b * 10) / 10; // 0.3
文字列の改行記述に関する制限
文字列リテラル内で物理的な改行を直接使うと構文エラーになります。
// 誤り
// let str = "Hello
// World";
// 正しい方法:エスケープシーケンスを使用
let str = "Hello \
World";
あるいはテンプレートリテラル(バッククォート)を使うことで、自然な改行が可能になります。
let multiline = `Hello
World`;
不要なセミコロンによる制御フローの破綻
if文の直後に誤ってセミコロンを置くと、その後のブロックは常に実行されます。
if (false); {
console.log("ここは必ず実行される");
}
これは、if文が空の処理になり、後続の{}がただのコードブロックとして扱われるためです。
return 文の自動セミコロン挿入(ASI)
JavaScriptは行末にセミコロンがない場合、文の終わりと判断して自動的に挿入することがあります。これにより、return後に値が続くように書いても、実際にはundefinedが返されることがあります。
function getValue() {
return
"Hello";
}
console.log(getValue()); // undefined
正しくはreturnと値を同じ行に記述する必要があります。
function getValue() {
return "Hello";
}
配列に対する名前付きインデックスの使用
JavaScriptの配列は数値インデックス専用です。文字列キーを使用すると、内部的に通常のオブジェクトに変換され、配列としての機能が失われます。
let arr = [];
arr[0] = "Alice";
arr["name"] = "Bob";
console.log(arr.length); // 1("name"はカウントされない)
console.log(arr.name); // "Bob"
console.log(arr["name"]); // "Bob"
名前付きプロパティが必要なら、純粋なオブジェクトを使用すべきです。
配列・オブジェクトの末尾カンマの取り扱い
末尾にカンマを置くことは構文的には有効ですが、古い環境では問題になる可能性があります。また、配列の長さに影響を与える場合もあります。
// 避けたほうがよい
let items = [1, 2, 3, ];
// 推奨
let items = [1, 2, 3];
オブジェクトでも同様です。
let config = {
mode: "strict",
debug: true
}; // カンマなし
null と undefined の違い
nullは「意図的に空である」ことを示す値であり、undefinedは「未定義」または「値がまだ割り当てられていない」状態を表します。存在しない変数に対してtypeofを使わずに比較するとエラーになります。
// 危険:myObjが未定義だとエラー
// if (myObj !== null && myObj !== undefined)
// 安全なチェック
if (typeof myObj !== "undefined" && myObj !== null) {
// 処理
}
ブロックスコープの理解
varで宣言された変数は関数スコープを持ち、ブロック(例:forループ内)での宣言でも関数全体で有効になります。
for (var i = 0; i < 5; i++) {
// ロジック
}
console.log(i); // 5(ループ終了後もアクセス可)
これを防ぐには、letまたはconstを使用してブロックスコープを明確にします。
for (let j = 0; j < 5; j++) {
// ロジック
}
// console.log(j); // ReferenceError: j is not defined