ウェブエンジニア問題集

変数宣言とスコープ — var・let・constの違いと使い分け

JavaScriptを書きはじめて最初に出会うのが「変数」です。そして最初につまずきやすいのが、変数を宣言する3つのキーワード var / let / const の違いです。「結局どれを使えばいいの?」という疑問に、この章ではっきり答えを出します。

あわせて、変数が「どこから見えるか」を決めるスコープと、宣言が巻き上げられるホイスティングという、var / let / const の違いの根っこにある仕組みも押さえます。

学習者学習者

varletconst って3つもあるけど、どれを使えばいいの?サンプルコードによってバラバラで混乱する…。

先に結論だけ言うと、「基本は const、再代入が必要なときだけ letvar は使わない」 です。なぜそうなるのかを、これから順に見ていきましょう。

変数とは — 値に名前をつける箱

変数は、数値や文字列などの値に名前をつけて保存しておく仕組みです。一度名前をつけておけば、後からその名前で値を参照できます。

const name = '太郎';
const age = 25;
 
console.log(name); // "太郎"
console.log(age); // 25
値に名前をつけて保存するイメージ

3つの宣言方法 — まず全体像

var / let / const の違いを先に表でつかみましょう。詳細はこのあと1つずつ解説します。

キーワード再代入再宣言スコープ使う?
const❌ 不可❌ 不可ブロック基本これ
let⭕ 可❌ 不可ブロック⭕ 再代入が要るときだけ
var⭕ 可⭕ 可関数❌ 使わない(古い)

const — 再代入できない(デフォルトで使う)

const で宣言した変数には、後から別の値を代入できません(再代入不可)。

const tax = 0.1;
tax = 0.08; // ❌ TypeError: Assignment to constant variable.

「値が途中で変わらない」ことが保証されるため、コードが読みやすく・安全になります。だからこそまず const を選ぶのが現代の基本方針です。

先生先生

「あれ、この変数あとで書き換わるのかな?」と読み手が悩まずに済む。それだけでコードはぐっと読みやすくなるんだ。

注意: constでも「中身」は変えられる

ここが重要な誤解ポイントです。const が禁止するのは変数への再代入だけです。配列やオブジェクトの中身(要素・プロパティ)の変更は可能です。

const user = { name: '太郎' };
user.name = '花子'; // ⭕ OK(プロパティの変更は許される)
user.age = 30; // ⭕ OK
 
user = { name: '次郎' }; // ❌ NG(変数そのものへの再代入は不可)
 
const list = [1, 2, 3];
list.push(4); // ⭕ OK([1, 2, 3, 4] になる)
学習者学習者

const の配列に push できるの…!?「定数」なのに中身を変えられるのは意外…。

const は「箱の中身を絶対に変えない」ではなく「箱に入れ替えない(同じ値を指し続ける)」と捉えると正確です。オブジェクトの場合、「同じオブジェクトを指し続ける」ことは保たれますが、そのオブジェクトの中身は変えられる、というわけです。

let — 再代入できる

カウンターやループのように、値を更新していく必要があるときは let を使います。

let count = 0;
count = count + 1; // ⭕ 再代入OK
count += 1; // ⭕
 
console.log(count); // 2

「この変数は後で変わる」という意図を let が示してくれます。逆に言えば、再代入しない変数にまで let を使うと意図がぼやけるので、迷ったら const から始めましょう。

var — 使わない(古い書き方)

var はJavaScript初期からある宣言方法ですが、後述するスコープの広さ巻き上げの挙動が直感に反し、バグの温床でした。let / const(ES2015で追加)が登場した今、新しく書くコードで var を使う理由はありません

スコープ — 変数が見える範囲

スコープとは、宣言した変数がどこから参照できるかの範囲のことです。let / constvar で、この範囲が決定的に違います。

ブロックスコープ(let / const)

let / const は、{ }(ブロック)の中で宣言すると、そのブロックの中でしか使えません

{
  const message = 'こんにちは';
  console.log(message); // ⭕ "こんにちは"
}
console.log(message); // ❌ ReferenceError: message is not defined

if 文や for 文の { } も同じです。範囲が狭く限定されるので、変数が思わぬ場所から書き換えられる事故を防げます。

関数スコープ(var)

一方 var は、{ } のブロックを無視し、それを囲む関数全体で有効になります。

function example() {
  if (true) {
    var x = 1; // ifブロックの中で宣言したのに…
  }
  console.log(x); // 1(関数内ならブロックの外でも見えてしまう)
}
学習者学習者

if の中で宣言したのに外から見える…?これは確かに混乱しそう。

先生先生

そう、ここが var の罠。let / const なら「宣言したブロックの中だけ」という直感どおりに動くから安全なんだ。

変数の見える範囲を確認するイメージ

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

JavaScriptでは、変数や関数の宣言がコードの先頭に引き上げられたかのように扱われます。これを巻き上げ(hoisting)と呼びます。ここでも varlet / const で挙動が異なります。

var は undefined で巻き上がる

var は宣言が巻き上げられ、値の代入前でもエラーにならず undefined になります。これがバグを生みます。

console.log(x); // undefined(エラーにならない=バグに気づきにくい)
var x = 5;
console.log(x); // 5

let / const は「使う前に触るとエラー」

let / const も巻き上げ自体は起きますが、宣言より前に使おうとするとエラーになります。この「宣言前は触れない区間」を TDZ(Temporal Dead Zone:一時的死角) と呼びます。

console.log(y); // ❌ ReferenceError: Cannot access 'y' before initialization
let y = 5;

使い分けの結論

ここまでをふまえた実務での判断はシンプルです。

flowchart TB
    A["変数を宣言したい"] --> B{"後で再代入する?"}
    B -->|"しない(ほとんどこれ)"| C["const を使う"]
    B -->|"する(カウンター等)"| D["let を使う"]
    A --> E["var は使わない"]
  1. まず const で書く
  2. 再代入が必要だと分かったら、その変数だけ let に変える
  3. var は使わない

早見表

知りたいこと答え
基本はどれ?const
再代入したいlet
var は?使わない
const の配列に push できる?できる(中身の変更は可、再代入は不可)
ブロックスコープなのは?let / const
関数スコープなのは?var
宣言前に使うと?varundefined / letconst→エラー(TDZ)

よくあるハマりどころ

変数まわりのハマりどころに慌てるイメージ

1. const だから中身も変わらない、という思い込み

前述のとおり、const でも配列・オブジェクトの中身は変更できます。「再代入できない」だけだと正確に理解しておきましょう。

2. ループ内の var で値が共有される

var は関数スコープのため、ループで作った関数がすべて同じ変数を参照してしまう古典的なバグがあります。let ならループごとに別の変数になり、意図どおり動きます。

// ❌ var: すべて 3 が出力される
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 3, 3, 3
 
// ⭕ let: 0, 1, 2 が出力される
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}
// 0, 1, 2

3. 同じ名前で再宣言してしまう

var は同じ名前で何度でも再宣言できてしまい、うっかり上書き事故が起きます。let / const は同じスコープでの再宣言をエラーにしてくれるため、ミスに気づけます。

var a = 1;
var a = 2; // ⭕ エラーにならない(危険)
 
let b = 1;
let b = 2; // ❌ SyntaxError(安全)

ちゃんと使うためのポイント

  • 宣言は const を基本にし、再代入が必要なときだけ letvar は使わない
  • const が禁止するのは再代入だけ。配列・オブジェクトの中身は変更できる
  • let / constブロックスコープ{ } の中だけ)、var関数スコープで範囲が広い
  • 巻き上げにより、var は宣言前でも undefinedlet / const は宣言前に使うとエラー(TDZ)
  • 「エラーになってくれる」のはバグの早期発見につながる利点

次の章では、変数に入れる「値」そのもの——データ型 を扱います。文字列・数値・真偽値や null / undefined の違いを整理し、JavaScriptの土台を固めましょう。