値の入れ替え(スワップ)とは何か
分割代入を使うと、2つ以上の変数の値を“一時変数なし”で入れ替えられます。ここが重要です:左側のパターンに「新しい並び」を書き、右側に「古い並び」を配列で渡すだけ。読みやすく、バグが起きにくい定石です。
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a, b); // 2 1
JavaScript基本構文と評価の順序(なぜ安全に入れ替わるのか)
分割代入の右側([b, a])は、代入前の古い値を使って「先に」評価されます。その結果がタプル(配列)として作られ、左側の変数へ“同時に”割り当てられます。ここが重要です:右側の評価時点では a と b はまだ古い値なので、誤って同じ値を二重に入れることがありません。
let x = "left", y = "right";
// 右側 [y, x] は現在の x,y を材料に先に作られる
[x, y] = [y, x];
console.log(x, y); // "right" "left"
JavaScript3個以上や部分入れ替え(拡張パターン)
2個だけに限りません。任意の並び替えや回転(ローテーション)、一部だけの入れ替えも簡潔に書けます。ここが重要です:右側の配列は「古い順序」、左側は「新しい順序」を明示するだけです。
// 3つのローテーション:a→b, b→c, c→a
let a = 1, b = 2, c = 3;
[a, b, c] = [b, c, a];
console.log(a, b, c); // 2 3 1
// 部分だけ入れ替え(配列の中身)
const arr = [10, 20, 30];
// 先頭と末尾を入れ替え
[arr[0], arr[2]] = [arr[2], arr[0]];
console.log(arr); // [30, 20, 10]
JavaScriptスキップ・残余と組み合わせる(柔軟な並び替え)
スワップと同じ“右で並べて左へ割り当てる”ルールで、要素のスキップや残余(rest)も活用できます。ここが重要です:必要な箇所だけ書いて、残りはそのまま保持する設計が読みやすくなります。
// 先頭と次を入れ替え、残りはそのまま
let [first, second, ...tail] = [1, 2, 3, 4];
[first, second] = [second, first];
const out = [first, second, ...tail];
console.log(out); // [2, 1, 3, 4]
JavaScriptオブジェクト・プロパティの入れ替え(名前で指す)
位置ではなく“名前”で意味を保ちながら入れ替えられます。ここが重要です:プロパティアクセス(obj.a など)を左側・右側に対称に書けば、意図が直感的に伝わります。
const user = { a: "Alice", b: "Bob" };
[user.a, user.b] = [user.b, user.a];
console.log(user); // { a: "Bob", b: "Alice" }
JavaScriptよくある落とし穴と回避策(重要ポイントの深掘り)
未定義や参照エラーに注意します。ここが重要です:右側の配列を作るタイミングで必要な値が揃っていないと例外になります。TDZ(一時的デッドゾーン)や未宣言変数を参照しないよう、宣言済みの変数だけを入れ替えましょう。
// NG:右側の評価でまだ宣言されていない変数を参照するとエラー
// [a, b] = [b, a]; // a, b を先に let/const で宣言していないとダメ
// OK:必ず事前に宣言
let a = 1, b = 2;
[a, b] = [b, a];
JavaScriptまた、同じ左辺を重複して書くと意図しない上書きになります。左辺の各“ターゲット”は一意に保ちましょう。
let x = 1, y = 2;
// NG:左で同じ変数を二度使うと混乱を招く
// [x, x] = [y, x]; // 意図不明&結果も期待通りにならない
// OK:ターゲットは一意に
[x, y] = [y, x];
JavaScriptオブジェクトの深いプロパティを入れ替える場合、途中が存在しない(undefined)とクラッシュするため、存在チェックや既定値で防御します。
const cfg = { ui: { left: "L" } };
// 右側の評価時点で cfg.ui.right が undefined でも入れ替え自体は可能
[cfg.ui.left, cfg.ui.right] = [cfg.ui.right ?? "R", cfg.ui.left];
console.log(cfg.ui); // { left: "R", right: "L" }
JavaScript実用例で理解を固める
// 1) ソートのスワップ(アルゴリズムの定番)
function bubbleSwap(arr) {
const a = arr.slice();
for (let i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
[a[i], a[i + 1]] = [a[i + 1], a[i]];
}
}
return a;
}
console.log(bubbleSwap([3, 2, 1])); // [2, 3, 1](1パスだけの例)
// 2) スタックのトップ2を入れ替える
const stack = [10, 20, 30];
const top = stack.length - 1;
[stack[top], stack[top - 1]] = [stack[top - 1], stack[top]];
console.log(stack); // [10, 30, 20]
// 3) 3点ローテーション(UI の位置変更など)
let left = "L", center = "C", right = "R";
[left, center, right] = [center, right, left];
console.log(left, center, right); // C R L
// 4) オブジェクトの主従入れ替え(意味保持)
const pair = { primary: "A", secondary: "B" };
[pair.primary, pair.secondary] = [pair.secondary, pair.primary];
console.log(pair); // { primary: "B", secondary: "A" }
JavaScriptまとめ
値の入れ替えの核心は「右側で“古い値から新しい並び”を先に作り、左側へ一括で割り当てる」ことです。一時変数なしで安全・短く書け、2個でも3個でも応用可能。配列の要素やオブジェクトのプロパティにも適用でき、アルゴリズムや UI の並び替えで定番パターンになります。宣言済みの変数だけを対象にし、左辺ターゲットは一意に、存在しない深いプロパティには既定値やガードを添える。これを押さえれば、初心者でも“迷わず正しく”値の入れ替えを使いこなせます。
