var と ES6(let/const)の根本的な違い
var は“関数スコープ+巻き上げ(hoisting)あり、再宣言可能”という古い挙動、let/const は“ブロックスコープ+TDZ(宣言前アクセス不可)、再宣言不可”という安全な挙動です。ここが重要です:スコープが広く曖昧な var は意図しない共有や未初期化使用を招きます。実務では let/const を標準にし、var は原則使いません。
// var:同じ関数内ならどこでも見える(ブロックを跨ぐ)
if (true) { var a = 1; }
console.log(a); // 1
// let/const:ブロック内だけ
if (true) { let b = 2; }
console.log(typeof b); // "undefined"(見えない)
JavaScriptスコープの差(関数スコープ vs ブロックスコープ)
var は関数スコープで“ブロックを無視する”
var で宣言した変数は、同じ関数内なら if/for/switch のブロック外でも参照できます。ここが重要です:意図せず値が漏れて上書きされる事故の温床です。
function f() {
if (true) { var x = 10; }
return x; // 10(ブロック外から見える)
}
JavaScriptlet/const はブロックスコープで“内側に閉じる”
let/const は { } の内側だけ有効。スコープが小さいほど、衝突や未初期化の事故が減ります。
function g() {
if (true) { const y = 10; }
// return y; // ReferenceError(ブロック外)
}
JavaScript巻き上げ(hoisting)と TDZ の差
var は巻き上げで“宣言前でも undefined”
var は宣言が関数先頭に巻き上げられ、初期化前は undefined になります。静かに動くため、バグが隠れます。
console.log(v); // undefined(宣言前でも参照できてしまう)
var v = 5;
JavaScriptlet/const は TDZ で“宣言前アクセスがエラー”
宣言位置まで“存在しない扱い”のゾーン(TDZ)。未初期化の使用を即エラーで炙り出します。
console.log(n); // ReferenceError(TDZ)
let n = 5;
JavaScript再宣言と再代入(安全性の差)
var は同スコープで再宣言可能(上書きの温床)
同じ関数・同じブロックで var を再宣言してもエラーになりません。意図しない上書きが起きます。
var a = 1;
var a = 2; // OK(静かに上書き)
JavaScriptlet/const は再宣言不可(コンパイル時に止める)
同スコープで同名を宣言すると SyntaxError。ここが重要です:重複宣言を早期に防げます。
let b = 1;
// let b = 2; // SyntaxError(再宣言不可)
const c = 3;
// const c = 4; // SyntaxError
JavaScriptループとクロージャ(非同期での挙動差)
var はループ後の最終値を共有しがち
非同期コールバック内で、すべて同じ値になってしまう典型事故。
for (var i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), 0); // 4, 4, 4(最後の値を参照)
}
JavaScriptlet は“反復ごとに新しい束縛”で期待通り
ここが重要です:非同期でもインデックスが正しく閉じ込められます。
for (let i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), 0); // 1, 2, 3
}
JavaScriptグローバルとの関係(window へぶら下がるか)
var のグローバルは window(または globalThis)にプロパティ化
ブラウザで var をグローバルに置くと、window に現れます。名前衝突の原因です。
var gv = 1;
console.log(window.gv); // 1
JavaScriptlet/const のグローバルは window に現れない
ここが重要です:名前空間汚染を避けやすい。とはいえ、グローバル乱用は避けるのが原則。
let gl = 2;
console.log(window.gl); // undefined
JavaScriptswitch とブロック再宣言の扱い
var は同名でも“何度でも宣言できる”
switch 全体が単一ブロックでも、var は再宣言でエラーになりません。
switch (k) {
case "A": var x = 1; break;
case "B": var x = 2; break; // OK(上書き)
}
JavaScriptlet/const は case ごとに { } で分ける必要
同名を宣言したいならブロックを分ける。ここが重要です:安全なスコープ分割を意識する。
switch (k) {
case "A": { const x = 1; break; }
case "B": { const x = 2; break; }
}
JavaScriptdelete とインターナル(細かな違いに注意)
var で作ったグローバルは delete できないことがある
プロパティ属性の違いで、delete が効かないケースが生じます。設計が不透明になりがちです。
var x = 1;
// delete x; // false(削除できないことがある)
JavaScriptlet/const は“変数”として管理される
プロパティではなく、環境レコードの束縛。delete 対象ではないため、意図しない削除や挙動差が起きにくい。
実務の指針(いつ何を使うか)
原則:let/const を使い、var は使わない
- スコープを最小化して安全性を高める(ブロックスコープ)。
- 宣言前使用を TDZ で阻止して、未初期化バグをなくす。
- 再宣言を禁止し、上書きの事故を防ぐ。
// 良い例:意図が明確で安全
function sum(items) {
let total = 0; // 可変値は let
for (const n of items) { // 反復内の値は const
total += n;
}
return total;
}
JavaScript既存コードで var がある場合の移行ポイント
- var を let/const に置換し、ブロックに閉じる。
- ループのクロージャ問題は let によって自然解決。
- グローバル汚染を解消(window から切り離す)。
- switch/case は { } でブロック化し、同名宣言を安全に。
まとめ
var と ES6(let/const)の核心差は「スコープの広さ・宣言前の挙動・再宣言の可否・非同期での束縛」にあります。var は関数スコープ+巻き上げで曖昧になりがち、let/const はブロックスコープ+TDZでバグを早期に止めます。ここが重要です:実務では“まず const、必要なら let”、var は原則使わない。宣言は使う直前、スコープを最小化し、case はブロック化。これだけで初心者でも、読みやすく安全な ES6+ のコードに一気に近づけます。
