JavaScript | ES6+ 文法:変数・宣言の進化 – var との挙動差

JavaScript
スポンサーリンク

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(ブロック外から見える)
}
JavaScript

let/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;
JavaScript

let/const は TDZ で“宣言前アクセスがエラー”

宣言位置まで“存在しない扱い”のゾーン(TDZ)。未初期化の使用を即エラーで炙り出します。

console.log(n); // ReferenceError(TDZ)
let n = 5;
JavaScript

再宣言と再代入(安全性の差)

var は同スコープで再宣言可能(上書きの温床)

同じ関数・同じブロックで var を再宣言してもエラーになりません。意図しない上書きが起きます。

var a = 1;
var a = 2; // OK(静かに上書き)
JavaScript

let/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(最後の値を参照)
}
JavaScript

let は“反復ごとに新しい束縛”で期待通り

ここが重要です:非同期でもインデックスが正しく閉じ込められます。

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
JavaScript

let/const のグローバルは window に現れない

ここが重要です:名前空間汚染を避けやすい。とはいえ、グローバル乱用は避けるのが原則。

let gl = 2;
console.log(window.gl); // undefined
JavaScript

switch とブロック再宣言の扱い

var は同名でも“何度でも宣言できる”

switch 全体が単一ブロックでも、var は再宣言でエラーになりません。

switch (k) {
  case "A": var x = 1; break;
  case "B": var x = 2; break; // OK(上書き)
}
JavaScript

let/const は case ごとに { } で分ける必要

同名を宣言したいならブロックを分ける。ここが重要です:安全なスコープ分割を意識する。

switch (k) {
  case "A": { const x = 1; break; }
  case "B": { const x = 2; break; }
}
JavaScript

delete とインターナル(細かな違いに注意)

var で作ったグローバルは delete できないことがある

プロパティ属性の違いで、delete が効かないケースが生じます。設計が不透明になりがちです。

var x = 1;
// delete x; // false(削除できないことがある)
JavaScript

let/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+ のコードに一気に近づけます。

タイトルとURLをコピーしました