let・const・varの違い|使い分けとスコープの基本

let・const・varの違いとは

JavaScriptで変数を宣言するキーワードは var、let、const の3つです。var は昔からある書き方で、let と const は ES2015(ES6)で追加されました。現在の実務では「基本は const、再代入が必要なときだけ let、var は新規コードでは使わない」が定番です。

違いを一言でまとめると、次の3点です。

  • var は関数スコープ、let と const はブロックスコープ
  • const は再代入できない(let と var は再代入できる)
  • let と const は宣言前にアクセスするとエラーになる(var は undefined になる)

基本(構文・仕組み)

項目 var let const
スコープ 関数 ブロック {} ブロック {}
再宣言 できる できない できない
再代入 できる できる できない
宣言前の参照 undefined ReferenceError ReferenceError
初期化 省略可 省略可 必須

「ブロック」とは if、for、{ } で囲まれた範囲のことです。var はブロックを無視して関数全体に効くため、意図しない場所から値が見えてしまいます。

const price = 1000;      // 再代入しない値
let count = 0;           // 後で変わる値
count = count + 1;

サンプルコード(実行結果つき)

スコープの違い

function f() {
  if (true) {
    var a = 1;
    let b = 2;
    const c = 3;
  }
  console.log(typeof a, typeof b, typeof c);
}
f(); // number undefined undefined

if の外から var の a だけが見えています。let・const はブロックの外では存在しないため、typeof は undefined を返します。

巻き上げ(ホイスティング)

console.log(typeof v1); // undefined
var v1 = 1;

console.log(l1);        // ReferenceError: Cannot access 'l1' before initialization
let l1 = 1;

var は宣言が先頭に引き上げられ、値だけが後から入ります。let・const も宣言自体は巻き上げられますが、初期化されるまでの区間(TDZ:一時的デッドゾーン)で触るとエラーになります。バグに早く気づけるので、エラーになる側が安全です。

ループとクロージャ

const r1 = [];
for (var i = 0; i < 3; i++) { r1.push(() => i); }
console.log('var:', r1.map(f => f())); // [ 3, 3, 3 ]

const r2 = [];
for (let j = 0; j < 3; j++) { r2.push(() => j); }
console.log('let:', r2.map(f => f())); // [ 0, 1, 2 ]

var は全ての関数が同じ i を共有するため全部 3 になります。let は反復ごとに新しい束縛が作られるので、期待どおり 0, 1, 2 になります。

constの注意:中身は変えられる

const o = { n: 1 };
o.n = 2;            // OK: { n: 2 }
o = 3;              // TypeError: Assignment to constant variable.

const が固定するのは「変数と値の結びつき」であり、オブジェクトの中身ではありません。中身も固定したいときは Object.freeze() を使いますが、浅い凍結である点に注意してください(オブジェクトのコピー も参照)。

動作確認(検証環境と結果)

  • 環境: Node.js v24.19.0(Windows 11)、node で上記コードを実行
  • 結果: 上記コメントの出力と一致(number undefined undefined、[ 3, 3, 3 ]、[ 0, 1, 2 ]、各エラーメッセージ)
  • 再宣言 let x=1; let x=2; は SyntaxError: Identifier 'x' has already been declared を確認
  • 検証ログ: C:Tempks2techtestsjs-let-const-var.md
  • ブラウザでの実行は未検証(挙動は仕様で定められているため同じ結果になる想定です)

let・const・varの違いの注意点

  • const は「不変」ではなく「再代入不可」です。配列やオブジェクトの push・プロパティ変更は可能です。
  • 通常のスクリプト(ES Modules 以外)のトップレベルで var 宣言すると window のプロパティになりますが、let・const はなりません。
  • let を for の先頭で宣言すると反復ごとに別の束縛になります。外で宣言した let を使い回す場合は別物です。
  • 古いブラウザ(Internet Explorer など)は let・const に非対応です。現在は多くのサイトでサポート対象外になっていますが、案件の対応ブラウザは確認してください。

let・const・varの違いでよくあるミス

  • 何でも let で宣言してしまい、どの変数が変わるのか読みにくくなる。
  • const で宣言した配列に再代入しようとしてエラーになる(arr = [...] はNG、arr.push() はOK)。
  • switch の case 内で let を宣言して、同じブロックで名前が衝突する。case ごとに { } で囲むと回避できます。
  • 宣言前に関数を呼んでしまい、ReferenceError になる。

ブラウザ対応・バージョン

let・const は主要ブラウザの現行版で利用できます。詳細な対応バージョンは各MDNページの「ブラウザーの互換性」で確認してください(上記 sources 参照)。

let・const・varの違いのチェックリスト

  • [ ] 再代入しない変数は const にした
  • [ ] 再代入する変数だけ let にした
  • [ ] 新規コードに var を書いていない
  • [ ] ループ内の非同期処理・コールバックで変数が共有されていない
  • [ ] const のオブジェクトを「完全に固定」と誤解していない

let・const・varの違いのFAQ(よくある質問)

Q. varはもう使ってはいけませんか?
A. 動作はしますが、スコープが広く巻き上げの挙動も紛らわしいため、新規コードでは let・const を勧めます。既存コードの保守では var を見かけるので、読めることは必要です。

Q. 毎回constにするのは面倒では?
A. 先に const で書き、再代入が必要になったときに let へ変えるだけです。ESLintの prefer-const ルールで自動検出もできます。

Q. 関数の中だけで使う定数も大文字にすべき?
A. 慣習として、モジュール全体で使う固定値は MAX_COUNT のように大文字にすることがありますが、必須ではありません。チームの規約に従ってください。

筆者の見解(let・const・varの違い)

私見では、const を基本にする習慣は、コードの読みやすさに直結すると考えます。変数が再代入されないと分かれば、読む側は「この値は途中で変わらない」と安心して追えるためです。一方で、const をオブジェクトの不変化と誤解する初心者が多いので、教えるときは「再代入できないだけ」と繰り返し伝えるのが良いと思います。

let・const・varの違いの関連項目

出典(一次情報)

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