記事内にはプロモーションが含まれています

【JavaScript】スコープとは?var・let・constの違いや仕組みをわかりやすく解説

【JavaScript】スコープとは?var・let・constの違いや仕組みをわかりやすく解説 JavaScript

JavaScriptを学習していて、「変数を定義したはずなのに参照できない」「なぜか意図しない値に書き換わっている」といったバグに遭遇したことはありませんか?

これらの問題の多くは、「スコープ(変数の有効範囲)」 を正しく理解していないことが原因です。

特にJavaScriptには、古くからある var と、現在主流の let / const でスコープのルールが異なるという歴史的な背景があります。

この違いをあいまいにしたまま開発を進めると、思わぬ落とし穴にはまることになります。

この記事では、JavaScriptにおけるスコープの基本概念から、グローバルスコープとローカルスコープの違い、そして実務で必須となる「スコープチェーン」や「クロージャ」の入り口まで、豊富なサンプルコードを交えて徹底解説します。

【本記事の信頼性】
プロフィール
執筆者:マヒロ
  • 執筆者は元エンジニア
  • SES⇒大手の社内SE⇒独立
  • 現在はこじんまりとしたプログラミングスクールを運営
  • モットーは「利他の精神」

JavaScriptの「スコープ」とは何か?

プログラミングにおけるスコープとは、「その変数が使える有効範囲」のことです。

変数は、どこにでも自由にアクセスできるわけではありません。
定義した場所によって「見える場所」と「見えない場所」が決まっています。

スコープを理解することで、変数の名前被りを防いだり、セキュリティを高めたり、メモリを節約したりすることができます。

JavaScriptのスコープは、大きく分けて以下の2種類に分類されます。

  1. グローバルスコープ:プログラムのどこからでもアクセスできる範囲
  2. ローカルスコープ:特定の場所(関数やブロック)の中だけでアクセスできる範囲

グローバルスコープとグローバル変数

プログラムのトップレベル(関数の外側)で宣言された変数は、「グローバルスコープ」に属します。
これをグローバル変数と呼びます。

グローバル変数は、プログラム内のどこからでも参照・変更が可能です。

// グローバル変数
const globalVar = "私はどこからでも見えます";

function showGlobal() {
  console.log(globalVar); // 関数の中からでもアクセス可能
}

showGlobal();
console.log(globalVar); // もちろん外からでもアクセス可能

実行結果

私はどこからでも見えます
私はどこからでも見えます

globalVar は関数の外で定義されているため、showGlobal 関数の内側からも、外側からも問題なく参照できています。

便利に見えますが、どこからでも変更できてしまうため、意図しない書き換え(バグ)の原因になりやすく、むやみに使うべきではないとされています。

ローカルスコープとローカル変数

関数やブロック({})の内側で宣言された変数は、「ローカルスコープ」に属します。
これをローカル変数と呼びます。

ローカル変数は、そのスコープの外側からは「見えない(アクセスできない)」状態になります。

function showLocal() {
  const localVar = "私は関数の中だけです";
  console.log(localVar); // OK
}

showLocal();

// スコープ外からアクセスしようとするとエラーになる
// console.log(localVar); 
// ReferenceError: localVar is not defined

localVarshowLocal 関数の中で定義されています。

そのため、関数の外から console.log(localVar) を実行しようとすると、「変数が定義されていません」というエラーが発生します。

これがスコープによる保護の仕組みです。

スコープの種類:関数スコープとブロックスコープ

JavaScriptのローカルスコープには、さらに細かく「関数スコープ」と「ブロックスコープ」の2種類が存在します。

ここが、varlet / const の最大の違いであり、初心者が混乱しやすいポイントです。

関数スコープ(varの特徴)

昔ながらの変数宣言である var は、「関数スコープ」を持ちます。

これは、「関数(function)の中であれば、if文やfor文のブロック {} を無視してアクセスできる」という特徴です。

function varTest() {
  if (true) {
    var message = "varはブロックを無視します";
  }
  
  // if文のブロックの外なのにアクセスできてしまう
  console.log(message); 
}

varTest();

実行結果

varはブロックを無視します

var で宣言された message は、if 文のブロック {} の中にありますが、var は「関数全体」をスコープとするため、ブロックを抜けた後でも参照できてしまいます。

これにより、長い関数の中で変数の影響範囲が分かりにくくなる問題がありました。

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

現在主流の letconst は、「ブロックスコープ」を持ちます。

これは、「波括弧 {} で囲まれた範囲の中だけで有効」という、他の多くのプログラミング言語と同じ直感的なルールです。

function letTest() {
  if (true) {
    let message = "letはブロックの中に閉じこもります";
    const secret = "constも同様です";
    
    console.log(message); // OK
  }
  
  // ブロックの外からはアクセスできない(エラーになる)
  // console.log(message); 
  // ReferenceError: message is not defined
}

letTest();

let で宣言された message は、if 文のブロック {} の中でのみ有効です。

そのため、ブロックの外側で console.log しようとするとエラーになります。

変数の有効範囲を最小限に抑えられるため、バグが発生しにくく、コードの保守性が高まります。

スコープチェーンの仕組みを理解する

JavaScriptには、ある変数を探すときに、「自分のスコープになければ、一つ外側のスコープを探しに行く」という仕組みがあります。これをスコープチェーンと呼びます。

外側の変数は見えるが、内側の変数は見えない

スコープチェーンは「内側から外側」への一方通行です。

const outerVar = "外側の変数";

function outerFunction() {
  const innerVar = "内側の変数";

  function innerFunction() {
    // 1. 自分のスコープ内に innerVar はないか? → ない
    // 2. 一つ外(outerFunction)にあるか? → あった!
    console.log(innerVar); 

    // 1. 自分のスコープ内に outerVar はないか? → ない
    // 2. 一つ外(outerFunction)にあるか? → ない
    // 3. さらに外(グローバル)にあるか? → あった!
    console.log(outerVar);
  }

  innerFunction();
}

outerFunction();

実行結果

内側の変数
外側の変数

innerFunction は、自分自身の中で変数が定義されていなくても、親である outerFunction、さらにその親であるグローバルスコープへと順番に変数を探しに行きます。

逆に、outerFunction から innerFunction の中の変数を覗くことはできません。

変数名が被った場合(シャドーイング)

もし、内側と外側で同じ名前の変数が定義されていた場合どうなるでしょうか?

答えは、「より内側の変数が優先」されます。

const text = "グローバル";

function checkScope() {
  const text = "ローカル"; // 同じ名前で定義
  console.log(text);
}

checkScope();
console.log(text);

実行結果

ローカル
グローバル

checkScope 関数の中では、自身のスコープにある text(値:”ローカル”)が見つかった時点で探索を終了します。

そのため、外側の text(値:”グローバル”)は隠されてしまいます。

これを「変数のシャドーイング」と呼びます。

関数の外に出れば、ローカル変数はもう存在しないため、グローバル変数の text が参照されます。

レキシカルスコープ(静的スコープ)とは?

JavaScriptのスコープにおいて重要な概念に「レキシカルスコープ」があります。

これは、「関数がどこで実行されたか」ではなく、「どこで定義されたか」によってスコープが決まるというルールです。

const value = "グローバル";

function funcA() {
  console.log(value);
}

function funcB() {
  const value = "ローカル(funcB)";
  // funcBの中で funcA を呼び出す
  funcA();
}

funcB();

実行結果

グローバル

funcB の中で funcA が呼び出されていますが、funcAfuncB の変数 value を使いません。

なぜなら、funcA定義された場所(コード上で書かれた場所)の外側にあるのは、グローバルスコープの value だからです。

「どこから呼ばれたか」に関係なく、コードを書いた時点の階層構造でスコープが決まることを覚えておきましょう。

JavaScriptのスキルを活かして年収を上げる方法

以上、JavaScriptのスコープについてわかりやすく解説してきました。

なお、JavaScriptのスキルがある場合には、「副業で稼いで年収を上げる」といったことが可能です。

JavaScriptのスキルを持つ人は多いものの、その分案件数も多く、副業エージェントやフリーランスエージェントを利用することで予想外の高単価案件が見つかることもあります。

エージェントサービスは、登録も利用もすべて完全無料なので、どんな求人や案件があるのか気になる方は、気軽に利用してみるとよいでしょう。