変数宣言文の違い
JavaScriptにおける変数宣言方式とスコープ(有効範囲)の理解はJavaScript開発で必須です。
本稿ではJavaScriptの変数宣言文(Statement)であるvar、let、constの違いを一目で把握できるように比較して説明します。
三種類の変数宣言文
JavaScriptで変数を初めて定義(または設定)することを「変数を宣言する」と言います。
変数を宣言するにはJavaScriptの規則に従って宣言する必要があります。JavaScriptでは変数を宣言する際に「変数宣言文(Statement)」を用います。
変数宣言文は一つだけではなく、三種類の宣言文があります。以下がその三種類の宣言文です。
varletconst
JavaScriptはこれら三種類の変数宣言文を提供することで、開発者が変数を宣言する際にコードの利便性、安定性、衝突の防止、そして予期しない動作の回避を実現できるよう選択肢を用意しています。
それぞれの変数宣言文の特徴と違いを理解することは、JavaScript開発において非常に重要です。
var、let、constの違いを「初期化」、「スコープ」、「再宣言および再代入」、「ホイスティング」の観点から比較し、各宣言文がどのような状況で適しているかを一目で把握できるように説明します。最後に「JavaScriptにおける変数宣言方法とスコープの重要性」について触れます。
初期化(Initialization)
JavaScriptで変数が宣言される際に値を割り当てる過程を「初期化」と言います。
| 比較項目 | var |
let |
const |
|---|---|---|---|
| 初期化 | 関係なし | 関係なし | 必ず初期化する必要がある |
| 初期化しない場合 | 参照時にJavaScriptエンジンが自動的に割り当てる初期値であるundefinedが代入されます |
参照時にJavaScriptエンジンが自動的に割り当てる初期値であるundefinedが代入されます。 |
エラーが発生する |
varやletで宣言した変数は、宣言時に値を割り当てても割り当てなくても構いません。値を割り当てない場合、参照時にJavaScriptエンジンが自動的にundefinedを割り当てます。
一方、constは宣言と同時に必ず初期化する必要があり、値を割り当てない場合はエラーが発生します。
例
var
var a = 10;
console.log(a); // 出力: 10
var b;
console.log(b); // 出力: undefined
let
let a = 10;
console.log(a); // 出力: 10
let b;
console.log(b); // 出力: undefined
const
const宣言 - 必ず初期化する必要がある
const age = 30;
const宣言 - 初期値を割り当てないとエラー
const age; // Uncaught SyntaxError: Missing initializer in const declaration
スコープ(Scope)
スコープ(Scope)とは「範囲」または「領域」を意味し、ここでは変数や関数にアクセスできる有効なコードの範囲を指します。
| 比較項目 | var |
let |
const |
|---|---|---|---|
| スコープ | グローバル(全域)スコープまたは関数スコープを持つ | グローバル(全域)スコープまたはブロック({})スコープを持つ |
グローバル(全域)スコープまたはブロック({})スコープを持つ |
var
varで宣言された変数は、グローバルスコープまたは関数スコープを持ちます。
- 関数の外で
varで宣言された変数は、コード全体で有効なグローバル(全域)スコープを持ちます。 - 関数内で
varで宣言された変数は、当該関数およびその下位関数内で有効な関数スコープを持ちます。 if文やfor文など、すべてのブロック({})はvarで宣言された変数のスコープにまったく影響を与えません。
varで宣言した変数は、関数の外で宣言された場合はグローバルで使用可能(有効)であり、関数内で宣言された場合は当該関数内でのみ使用可能(有効)です。
let, const
letやconstで宣言された変数は、グローバル(全域)スコープまたはブロック({})スコープを持ちます。
- ブロックの外で
letやconstで宣言された変数は、コード全体で有効なグローバル(全域)スコープを持ちます。 - ブロック内で
letやconstで宣言された変数は、当該ブロックおよび下位ブロック内で有効なブロックスコープを持ちます。
letやconstで宣言した変数は、ブロックの外で宣言された場合はグローバルで使用可能(有効)であり、ブロック内で宣言された場合は当該ブロック内でのみ使用可能(有効)です。
ブロック({})の意味
JavaScriptにおけるブロックとは、中括弧{}で囲まれたコード領域を指します。
以下はブロックの一般的な形です。
if () { // ifブロック
// ...
} else { // elseブロック
// ...
}
function exampleFn() { // 関数ブロック
// ...
}
{ // 任意の独立したブロック(通常あまり使用されません)
let temp = 10; // tempはこのブロック内でのみ有効
}
for () { // forブロック
// ...
}
しかし、中括弧がなくても文法上中括弧が必要であるが省略された(ifやfor文の後の単一文)コード領域も概念上はブロックに該当しますが、実際のブロックではないため、letやconstで変数を宣言することはできません。
例
var
/* グローバル(全域)スコープ */
var globalVar = "グローバル変数"; // コード全体で有効な変数
function exampleFunction() {
/* 関数スコープ */
var functionVar = "関数内変数"; // 関数内全体で有効な変数
if (true) {
var blockVar = "ブロック内変数"; // if文のブロック({})内で宣言されたが、関数内全体で有効
console.log(functionVar); // 出力: "関数内変数"
console.log(blockVar); // 出力: "ブロック内変数"
}
// if文のブロック({})内で宣言されたが、関数内全体で有効か確認
console.log(blockVar); // 出力: "ブロック内変数"
}
exampleFunction();
console.log(globalVar); // 出力: "グローバル変数"
console.log(functionVar); // ReferenceError発生(関数外では有効でない、functionVar is not defined)
let
/* グローバル(全域)スコープ */
let globalVar = "グローバル変数"; // このファイル(またはスクリプト)の最上位ブロック全体で有効
function exampleFunction() {
/* 関数ブロックスコープ */
let functionVar = "関数ブロック変数"; // 関数全体のブロックで有効
if (true) {
let innerBlockVar = "ifブロック変数";
console.log(functionVar); // 出力: "関数ブロック変数"
console.log(innerBlockVar); // 出力: "ifブロック変数"
}
// ifブロックスコープの外なので参照不可
// console.log(innerBlockVar); // ReferenceError (innerBlockVar is not defined)
}
if (true) {
let ifBlockVar = "ifブロック変数";
console.log(ifBlockVar); // 出力: "ifブロック変数"
}
exampleFunction();
console.log(globalVar); // 出力: "グローバル変数"
// console.log(functionVar); // ReferenceError (functionVar is not defined)
// console.log(ifBlockVar); // ReferenceError (ifBlockVar is not defined)
const
/* グローバル(全域)スコープ */
const globalVar = "グローバル変数"; // このファイル(またはスクリプト)の最上位ブロック全体で有効
function exampleFunction() {
/* 関数ブロックスコープ */
const functionVar = "関数ブロック変数"; // 関数全体のブロックで有効
if (true) {
const innerBlockVar = "ifブロック変数";
console.log(functionVar); // 出力: "関数ブロック変数"
console.log(innerBlockVar); // 出力: "ifブロック変数"
}
// ifブロックスコープの外なので参照不可
// console.log(innerBlockVar); // ReferenceError (innerBlockVar is not defined)
}
if (true) {
const ifBlockVar = "ifブロック変数";
console.log(ifBlockVar); // 出力: "ifブロック変数"
}
exampleFunction();
console.log(globalVar); // 出力: "グローバル変数"
// console.log(functionVar); // ReferenceError (functionVar is not defined)
// console.log(ifBlockVar); // ReferenceError (ifBlockVar is not defined)
再宣言(Redeclaration)および再代入(Reassignment)
「再宣言」とは、同じスコープ内で同一の識別子(変数名)で変数を再び宣言することを指します。
「再代入」とは、同じスコープ内で同一の識別子(変数名)に対して代入演算子(=)を使用して変数の値を変更することを指します。
| 比較項目 | var |
let |
const |
|---|---|---|---|
| 再宣言 | 可能 | 不可能 | 不可能 |
| 再代入 | 可能 | 可能 | 不可能 |
例
var
varで宣言された変数は、同じスコープ内で再宣言(Redeclaration)および再代入(Reassignment)が可能です。
既にvarで宣言された変数の値は、いつでも新しい値に変更可能であり、同一の識別子(変数名)で再宣言してもエラーは発生せず、最後に割り当てられた値で上書きされます。
/* 初期宣言および値の割り当て */
var myValue = 10;
console.log(myValue); // 出力: 10
/* 再代入 */
myValue = 20;
console.log(myValue); // 出力: 20(値が変更される)
/* 再宣言(varを再使用) */
var myValue = "Hello";
console.log(myValue); // 出力: "Hello"(エラーなく再宣言され、値が上書きされる)
let
letで宣言された変数は、同じスコープ内で再宣言(Redeclaration)が不可能です。
同じスコープ内で既にletで宣言された変数を同一の識別子(変数名)で再宣言すると、エラーが発生します。
// 同じスコープ内でletを使用して同一の識別子(変数名)で再宣言するとエラーが発生
let exampleLet = 10;
let exampleLet = 100; // Uncaught SyntaxError: Identifier 'exampleLet' has already been declared
// 異なるスコープ内でletを使用して同一の識別子(変数名)で再宣言することは可能
let exampleLet = 10;
console.log(exampleLet); // 出力: 10
if (true) {
let exampleLet = 100;
console.log(exampleLet); // 出力: 100
}
console.log(exampleLet); // 出力: 10
letで宣言された変数の再代入は、変数を宣言したスコープに関わらずアクセス可能なスコープ内で、同一の識別子で変数の値を変更できます。
// グローバルスコープ
let globalVar = "グローバル変数";
console.log(globalVar); // "グローバル変数"
// 再代入可能
globalVar = "再代入されたグローバル変数";
console.log(globalVar); // "再代入されたグローバル変数"
if (true) {
// ブロックスコープ
let blockVar = "ブロック変数";
console.log(blockVar); // "ブロック変数"
// 再代入可能
blockVar = "再代入されたブロック変数";
console.log(blockVar); // "再代入されたブロック変数"
}
// ブロック外では参照不可
// console.log(blockVar); // ReferenceError
const
constで宣言された変数は、同じスコープ内で再宣言(Redeclaration)が不可能です。
同じスコープ内で既にconstで宣言された変数を同一の識別子(変数名)で再宣言すると、エラーが発生します。
// 同じスコープ内でconstを使用して同一の識別子(変数名)で再宣言するとエラーが発生
const exampleConst = 10;
const exampleConst = 100; // Uncaught SyntaxError: Identifier 'exampleConst' has already been declared
// 異なるスコープ内でconstを使用して同一の識別子(変数名)で再宣言することは可能
const exampleConst = 10;
console.log(exampleConst); // 出力: 10
if (true) {
const exampleConst = 100;
console.log(exampleConst); // 出力: 100
}
console.log(exampleConst); // 出力: 10
constで宣言された変数の再代入は、変数を宣言したスコープに関わらずアクセス可能なスコープ内であっても、同一の識別子に対して代入演算子(=)を使用して変数の値を変更することはできません。
=)を使用した再代入は不可
const exampleConst = 10;
/* 同一の識別子に代入演算子(=)を使用して変数の値を変更することはできません。 */
exampleConst = 100; // Uncaught TypeError: Assignment to constant variable.
しかし、変数の値がオブジェクトや配列のように参照を保持するデータの場合は、プロパティや値の追加、更新、または削除が可能です。
以下はconstで割り当てられた通常のオブジェクト(Plain object)のプロパティ・値の追加、更新、削除に関する例です。
// 通常オブジェクトの宣言
const person = {
name: 'Alice',
age: 30
};
// 1. プロパティの追加
person.city = 'Seoul'; // 新しいプロパティを追加
console.log(person); // 出力: { name: 'Alice', age: 30, city: 'Seoul' }
// 2. プロパティの更新
person.age = 31; // 既存プロパティの値を更新
console.log(person); // 出力: { name: 'Alice', age: 31, city: 'Seoul' }
// 3. プロパティの削除
delete person.city; // プロパティを削除
console.log(person); // 出力: { name: 'Alice', age: 31 }
以下はconstで割り当てられた配列の要素の追加、更新、削除に関する例です。
// 配列の宣言
const numbers = [1, 2, 3];
// 1. 要素の追加
numbers.push(4); // 配列の末尾に要素を追加
console.log(numbers); // 出力: [1, 2, 3, 4]
// 2. 要素の更新
numbers[0] = 10; // 最初の要素の値を更新
console.log(numbers); // 出力: [10, 2, 3, 4]
// 3. 要素の削除
numbers.pop(); // 配列の末尾の要素を削除
console.log(numbers); // 出力: [10, 2, 3]
ホイスティング(Hoisting)
ホイスティングとは、JavaScriptにおいて変数や関数の宣言がスコープの先頭に「持ち上げられる」動作を指します。つまり、コードの実行段階では変数や関数の宣言が実際に記述された位置に関わらず、スコープの最上部に持ち上げられる現象です。
| 比較項目 | var |
let |
const |
|---|---|---|---|
| ホイスティング | 発生(ただし、変数が宣言された位置より前に参照されるとundefinedが割り当てられる) |
発生(ただし、変数が宣言された位置より前に参照されるとエラーが発生する) | 発生(ただし、変数が宣言された位置より前に参照されるとエラーが発生する) |
例
var
/*
* 以下のvar myNameで宣言されたmyName変数が
* スコープの最上部に位置するかのようにホイスティングされます。
* ただし、宣言された変数のみが持ち上げられ、
* 宣言時に割り当てた値は元の位置に残ります。
*
* 宣言時に値がないため、
* 実際に変数が宣言された位置より前に参照すると
* undefinedで初期化されます。
*/
/* 実際に変数が宣言された位置より前に参照 */
console.log(myName); // 出力: undefined(宣言のみがホイスティングされ、値はundefinedで割り当てられる)
/* varで宣言した変数の位置 */
var myName = "John";
let, const
letおよびconstもホイスティングされます。
ただし、実際に変数が宣言された位置より前に参照されると、JavaScriptエンジンはホイスティングされた変数の存在を確認できます。このため、変数が宣言される前にアクセスしてもエラーにはなりませんが、宣言順序が厳密に適用されるletおよびconstではエラーが発生します。
TDZ(Temporal Dead Zone、一時的死角)
このように宣言された変数のスコープ開始点から実際に変数が宣言された位置までをTDZ(Temporal Dead Zone、一時的死角)と呼びます。TDZはletおよびconstで宣言された変数にのみ適用されます。
// TDZ領域の開始
// ↓
console.log(myVar); // ReferenceError: Cannot access 'myVar' before initialization
// ↑
// TDZ領域の終了
const myVar = 10;
JavaScriptの変数宣言方法とスコープの重要性
- 正しい変数宣言方法とスコープの理解は、コードの正確性と安定性を保証する上で重要です。
- 変数の範囲を明確に指定し、変数の衝突を防ぐことで予期しない動作を回避できます。
- スコープを制御することで、変数の可視性とライフサイクルを管理できます。
- 変数宣言方法とスコープの正しい使用は、コードの可読性、保守性、再利用性を向上させます。
- 効率的な変数管理は、開発者の生産性を高め、デバッグを容易にします。
したがって、変数宣言方法とスコープの理解はJavaScript開発において必須であり、正しく活用することで安定かつ効率的なコードを作成できます。
効率的なJavaScriptを使用するには、varによる変数宣言方式は使用しないでください!
- 基本的に変数の宣言は
const宣言方式を使用し、 - 変数に再代入が必要な場合にのみ、
let宣言方式を使用してください。
ES6以降のJavaScriptを使用する場合、varによる変数宣言方式は避け、letおよびconstを使用して変数を宣言することが推奨されます。これにより、コードの可読性と保守性を向上させ、変数管理により慎重に取り組むことができます。
仕様書
| 仕様書 | |
|---|---|
| Variable Statement |
ECMAScript® 2026 Language Specification #sec-variable-statement |
ブラウザ互換性
| 変数宣言文 |
デスクトップ Chrome
|
デスクトップデスクトップ Edge
|
デスクトップ Firefox
|
Safari
|
Node.js
|
|---|---|---|---|---|---|
var
|
1 | 12 | 1 | 1 | 0.10 |
let
|
49 | 14 | 44 | 10 | 6 |
const
|
21 | 12 | 36 | 5.1 | 6 |