再宣言エラーとは何か
再宣言エラーは「同じスコープ内で、同じ名前の変数をもう一度宣言しようとすると起きるエラー」です。ES6 以降、let と const は“同スコープの再宣言”を禁止します。ここが重要です:重複宣言をコンパイル時(実行前)に止めることで、意図しない上書きやバグの温床を根絶します。
let a = 1;
// let a = 2; // SyntaxError: Identifier 'a' has already been declared
const b = 3;
// const b = 4; // SyntaxError: Identifier 'b' has already been declared
JavaScriptvar は同スコープで再宣言してもエラーにならず、静かに上書きされます(これが事故の元)。
var x = 1;
var x = 2; // OK(上書きされる)— これが意図しない挙動を生みやすい
JavaScriptどこで再宣言エラーが起きるか
同じブロック内で同名の let/const
ブロックスコープ({ } の中)で同じ名前をもう一度宣言するとエラーになります。ここが重要です:スコープの粒度を意識して、必要ならブロックを分ける/名前を変える。
{
const n = 1;
// const n = 2; // SyntaxError(同ブロック内の再宣言)
}
JavaScriptswitch 文(全体が単一ブロック)
switch 全体が1つのブロックとして扱われます。case ごとに同名の let/const を宣言すると再宣言エラー。対策は、case を { } で囲んで“ブロックを分ける”。
switch (kind) {
case "A": {
const msg = "alpha";
break;
}
case "B": {
const msg = "beta"; // 別ブロックなので OK
break;
}
}
JavaScriptimport された識別子(再宣言不可)
ES Modules の import で得た名前は“読み取り専用の束縛”で、同スコープで再宣言・再代入できません。
// import { foo } from "./lib.js";
// let foo = 1; // SyntaxError(import名の再宣言は不可)
JavaScript関数パラメータと同名の let/const
関数の引数名と同じ名前で、関数本体の同スコープに let/const を宣言すると再宣言エラー。
function f(id) {
// const id = 2; // SyntaxError(引数と同名の再宣言)
}
JavaScriptシャドーイングとの違い(隠すのはOK、同スコープの再宣言はNG)
“別スコープ”なら同名宣言は可能で、外側の変数を内側が“隠す(シャドーイング)”だけです。ここが重要です:シャドーイングは安全に使えるが、紛らわしい名前は避ける。
let label = "outer";
{
let label = "inner"; // 別ブロックなのでOK(外側を隠す)
console.log(label); // "inner"
}
console.log(label); // "outer"
JavaScript同じブロックでの重複宣言はNG。違いは“スコープが異なるかどうか”。
TDZ と再宣言の関係(宣言順で守られる安全)
TDZ(Temporal Dead Zone)は“宣言位置までその変数が存在しない扱い”のゾーンです。再宣言エラーとは別ですが、同名を内側ブロックで宣言する予定があると、その宣言より前に同名へ触れると ReferenceError(TDZ)が出ます。ここが重要です:宣言は使う直前に行い、宣言前参照を避ける。
let n = 5;
{
// console.log(n); // ReferenceError(このブロックで n を宣言予定 → TDZ)
let n = 10;
}
JavaScriptよくある落とし穴と対策
“var なら動くのに let/const だと落ちる”問題
var は再宣言OK/巻き上げで undefined 化するため“動いてしまう”。let/const はその曖昧さを禁止していると理解しましょう。対策は“同名を作らない、スコープを分ける、宣言を整理する”。
// 悪い例(意図が曖昧)
var name = "A";
var name = "B"; // 上書き
// 良い例(明確な意図)
let name = "A";
name = "B"; // 値を更新するなら“再代入”だけ行う(再宣言しない)
JavaScriptヘッダと本体の重複(関数パラメータ・for 文)
引数名や for ヘッダで使った識別子と同名の let/const を本体で宣言しない。必要なら別名にする。
for (let i = 0; i < 3; i++) {
// const i = 99; // SyntaxError(同スコープ再宣言)
}
JavaScriptモジュールのトップレベル衝突
同一ファイルのトップレベルは同スコープ。重複しやすいユーティリティ名は具体化し、1ファイル1束縛にする。
const parseDate = s => new Date(s);
// const parseDate = () => {}; // SyntaxError(同一スコープ再宣言)
JavaScript実務での指針(安全に書くための癖)
まず const、必要なら let(再宣言ではなく再代入)
不変が基本。値を変える必要があるときだけ let を使い、同スコープで“宣言を増やさない”。
const rate = 0.1;
let total = 0;
for (const it of items) {
const price = it.price ?? 0;
total += Math.round(price * (1 + rate));
}
JavaScriptスコープを分けて意図を隔離
switch の case、短い補助計算などは { } でミニブロックを作り、同名を安全に使い分ける。
{
const key = query.trim().toLowerCase();
rows = rows.filter(r => r.name.toLowerCase().includes(key));
} // key はこのブロック限り
JavaScript名前を具体化して衝突を防ぐ
抽象的な名前(data, result, value)は衝突しがち。文脈に合わせた具体名(userList, totalPrice, nextState)にする。
例題で理解を固める
switch の再宣言対策
function handle(kind) {
switch (kind) {
case "sum": {
const op = "add";
return op;
}
case "sub": {
const op = "minus"; // 別ブロックで安全
return op;
}
}
}
JavaScript再宣言せずに更新する(正しいパターン)
let count = 0; // 1回だけ宣言
count += 1; // 更新は再代入で
count += 1;
JavaScriptまとめ
再宣言エラーの核心は「同スコープで同名の let/const をもう一度宣言すると SyntaxError」であること。var は許すが曖昧で危険、let/const は厳格で安全。ここが重要です:宣言は1回、更新は再代入で行う/スコープを分けて同名の安全なシャドーイングにする/switch は case をブロック化。この癖を身につければ、初心者でも衝突を避け、意図が透けるES6+のコードを書けます。
