【JavaScript入門】変数のスコープを完全マスター!

こんにちは!Webコーダー・プログラマーの貝原(@touhicomu)です。

今回は、JavaScriptの変数のスコープについて解説したいと思います。

スコープにはグローバルスコープローカルスコープがあり、それぞれの使い分けが初心者の方には難しく感じるかと思います。

そこで、この記事では

・変数のスコープとは
・様々な変数のスコープ

という基本的な内容から

・変数のスコープが存在しないケース
・変数のスコープを定義する文法

という実践的な内容までを解説していきます。

変数のスコープについて正しく理解し、必要な場面で使いこなせるように、しっかり学習を進めていきましょう。

目次

変数のスコープとは

変数のスコープとは、その変数が参照できる範囲のことです。

スコープには、大きく分けて以下の2種類があります。

  • グローバルスコープ → ページ全体でどこからでも参照できる。
  • ローカルスコープ → ページ内の部分的な範囲のみ参照できる

特にローカルスコープの変数は、変数名を使い回すことができ、局所的な範囲での一時的な変数の利用に適しています。

具体的なコードでみていきましょう。

以下のコードでは、先頭行で、グローバル変数 valGlobal1 を定義し値を代入しています。

ここで、関数 scopeUse() 内では、関数内で一時的に使用するためにローカル変数 valGlobal1 を定義し値を代入しています。

valGlobal1 = "global1";
window.onload = function () {
   // スコープとは
    function scopeUse() {
        var valGlobal1 = "local";
        console.log("in local valGlobal1 = " + valGlobal1);
    }
    scopeUse();
}

実行結果:

in local valGlobal1 = local

以上のように、関数 scopeUse() のローカル変数 valGlobal1 が、グローバル変数 valGlobal1 よりも優先して使用されています。

まさに、この関数内など局所的な範囲内での変数の利用時に、グローバル変数であるかどうか考慮せずに、安全にローカル変数として使用できるところに、スコープのありがたみがあります。

様々な変数のスコープ

グローバルスコープ

グローバルスコープの変数は、JavaScriptのトップの位置に、varをつけずに、

valGlobal1 = “global1”;

もしくは、varをつけて、

var valGlobal2 = “global2”;

と宣言します。

グローバルスコープの変数は、ページ内のJavaScriptのどこからも参照可能です。

以下のコードでは、グローバルスコープの変数を、関数内から参照し、コンソールに出力しています。

また関数内でグローバルスコープの変数の値の変更も可能です。

valGlobal1 = "global1";
var valGlobal2 = "global2";
window.onload = function () {
    // グローバルスコープ
    function globalValue1() {
        console.log("valGlobal1 = " + valGlobal1);
        console.log("valGlobal2 = " + valGlobal2);

        // グリーバルスコープ変数を変更
        valGlobal2 = "global3";
        console.log("valGlobal2 modified = " + valGlobal2);
    }
    globalValue1();
}

実行結果:

valGlobal1 = global1
valGlobal2 = global2
valGlobal2 modified = global3

以上のように、関数内からグローバルスコープの変数を使用できていますね。

また、グローバル変数を使う場合は1点注意点があります。

グローバル変数はどこからでも使えるためとても便利ですが、どこからでも使える都合上変数名の衝突が起きやすいです。

たとえば、javascriptの新しいプラグインを入れた場合などに競合が発生し、予期せぬ動きになる場合があります。

多用しすぎには注意が必要のため、以降で説明するローカル変数の書き方を習得して一部のみグローバル変数を使うようにしてください。

関数スコープ

関数内でのスコープはローカルスコープの一つで関数スコープといいます。

関数内でvarをつけて定義した変数はローカル変数になります。

以下コードで確認してみましょう。

valGlobal1 = "global1";
var valGlobal2 = "global2";
window.onload = function () {
    // 関数スコープ
    function functionScopeValue1() {
        console.log("valGlobal1 = " + valGlobal1);
        console.log("valGlobal2 = " + valGlobal2);

        var valGlobal2 = "local2";
        console.log("valGlobal2 to local2 = " + valGlobal2);
    }
    functionScopeValue1();
}

実行結果:

valGlobal1 = global1
valGlobal2 = undefined
valGlobal2 to local2 = local2

関数内でもグローバル変数 valGlobal1 は使用できていますね。

実行結果の2行目は、

valGlobal2 = undefined

となっています。

valGlobal2の宣言は関数の4行目で行われています。

ローカル変数の宣言前にローカル変数と見なされるのは、関数内で定義した変数は、関数の頭で宣言したものとみなされるという仕様によります。

この仕様のことを、JavaScriptのホイスティング(巻き上げ)といいます。

ホイスティング対象の変数はvarつきでローカル変数として関数の4行目で定義されています。

var valGlobal2 = “local2”;

ローカル変数 valGlobal2 は、関数の4行目で値 “local2” が代入されていますが、ホイスティングが起こる関数の2行目では値が入っておらず、結果undefinedという値が表示されます。

関数の4行目でローカル変数に値を入れた後のconsole.log()の結果である、実行結果の3行目では、正しく値が設定され表示されていますね。

scriptタグのスコープ

scriptタグで分割して、グローバル変数を書いた場合にも、正しくグローバルスコープの変数として、ページ全体から変数を参照できます。

<script type="text/javascript">
    valGlobal1 = "global1";
    var valGlobal2 = "global2";
</script>
<script type="text/javascript">
    function globalValue2() {
        console.log("valGlobal1 = " + valGlobal1);
        console.log("valGlobal2 = " + valGlobal2);

        // グリーバルスコープ変数を変更
        valGlobal2 = "global3";
        console.log("valGlobal2 modified = " + valGlobal2);
    }
    globalValue2();

</script>

実行結果:

valGlobal1 = global1
valGlobal2 = global2
valGlobal2 modified = global3

以上のように、scriptタグをまたがっても、グローバル変数を参照できていますね。

またグローバル変数の値の変更もできています。

クロージャーのスコープ

クロージャーは関数を定義するのとほぼ同じですが、一つ上のブロックの変数にアクセスできます。

そのため、クロージャーと変数を組み合わせて簡潔に一時的に使用する関数を定義する場合などに使用されます。

クロージャーのスコープは、クロージャー内のブロックのスコープではなく、クロージャーの定義された一つ上のブロックのスコープです。

そのため、以下のコードでは、クロージャーの定義されているブロック内の変数 modifiedStr にクロージャーからアクセスできています。

クロージャーのこの特性はよく利用されます。

window.onload = function () {
    // クロージャースコープ
    function closureScope (str) {
        var modifiedStr = "***" + str + "***";
        var closure = function () {
            return "###" + modifiedStr + "###";
        }
        return closure();
    }
    console.log("closure return = " + closureScope("apple"));
}

実行結果:

closure return = ###***apple***###

以上のように、クロージャーの定義されたブロックの変数にクロージャーがアクセスできていますね。

即時関数のスコープ

即時関数とは、関数定義を技巧的に使用してブロックスコープを実現する方法です。

通常、JavaScriptにはブロックスコープは存在しませんが、即時関数を使用することで可能になります。

即時関数とは、

(function () {
・・・即時関数内コード・・・
})();

のようなコードになります。

window.onload = function () {
    //即時関数スコープ
    (function () {
        var immidateValue1 = "immidiate";
        function immidiateScope() {
            return "***" + immidateValue1 + "***";
        }
        console.log("immidate return = " + immidiateScope());
    })();
    console.log("immidateValue1 = " + immidateValue1);
}

実行結果:

immidate return = ***immidiate***
Uncaught ReferenceError: immidateValue1 is not defined

以上のように、即時関数内で定義された変数 immidateValue1 は即時関数内では使用できますが、即時関数外では、実行結果の2行目で immidateValue1 is not definedエラーが表示されているように使用できません。

これで、即時関数内はブロックスコープの変数を扱えることがわかりました。

変数のスコープが存在しないケース

if文のスコープ

JavaScriptではif文にはブロックスコープは存在しません

if文内で定義したローカル変数はif文のブロック外でも使用可能です。

以下、コードで確認してみましょう。

window.onload = function () {
    // if文でのスコープ
    if (true) {
        // if文内のスコープ
        var valIf = "if";
    }
    // if文外でもアクセス可能
    console.log("if out scope val = " + valIf);
}

実行結果:

if out scope val = if

以上のように、if文で定義したローカル変数をif文のブロック以外でも使用できてしまいますね。

for文のスコープ

JavaScriptではfor文にもブロックスコープは存在しません

for文内で定義したローカル変数はfor文のブロック外でも使用可能です。

以下、コードで確認してみましょう。

window.onload = function () {
    // for文でのスコープ
    for (var i = 0; i < 1; i++) {
        // for文内のスコープ
        var valFor = "for";
    }
    // for文外でもアクセス可能
    console.log("for out scope val = " + valFor);
}

実行結果:

for out scope val = for

以上のように、for文で定義したローカル変数をfor文のブロック以外でも使用できてしまいますね。

変数のスコープを定義する文法

with

JavaScriptでは、with句を使用することでブロックスコープを実現できます。

with句は、以下のように書きます。

with ({ valWith: “” } ) {
・・・文・・・
}

変数 valWith は、この with 句内でのみ有効なブロックスコープのローカル変数です。

window.onload = function () {
    // withによるローカルスコープ
    with ({ valWith: "" } ) {
        valWith = "with";
        console.log("with in scope val = " + valWith);
    }
    // withブロック外ではアクセスできない
    console.log("with out scope val = " + valWith);
}

実行結果:

with in scope val = with
Uncaught ReferenceError: valWith is not defined

実行結果の1行目は、with句内でのconsole.log()の出力で、正常に出力されています。

しかし、実行結果の2行目は、with句外で変数valWithを参照しているため、valWith is not definedエラーが出力されています。

このように、with句により、ブロックスコープの変数を使用できます。

let

letブロックスコープの変数を実現する文法です。

ただし、ECMAJavaScript2015(ES6)以降からのサポートとなります。

letの使い方は簡単で、今までローカル変数を定義していた方法varの代わりにletを使うだけです。

letで宣言された変数は、自動的にローカル変数になります。

以下、コードで確認してみましょう。

window.onload = function () {
    //letによるローカルスコープ
    // if文でのスコープ(ブロックスコープ)
    if (true) {
        // if文内のスコープ
        let valIf2 = "if2";
        console.log("let in scope val = " + valIf2);
    }
    // if文外ではアクセスできない
    console.log("if out scope val2 = " + valIf2);
}

実行結果:

let in scope val = if2
Uncaught ReferenceError: valIf2 is not defined

実行結果の1行目は、if文のブロック内でletで定義された変数の出力で、正常に処理されていますね。

実行結果の2行目は、if文のブロック外で変数にアクセスした場合で、valIf2 is not definedエラーが出力されていますね。

以上のように、letによりブロックスコープのローカル変数が定義できることが分かりました。

まとめ

いかがでしたでしょうか。

変数のスコープにはグローバルスコープロ-カルスコープがあり、変数名のかぶりを解消し、また、関数内など局所的に一時的に使用するローカル変数を利用するために便利でした。

特にローカルスコープは、コードの他の箇所に影響を与えないため、大変便利に使用できます。

ローカルスコープには、関数スコープ、クロージャーのスコープ、即時関数のスコープ、withのブロックスコープ、letのブロックスコープなどがありました。

種類がが多くて覚えることは大変ですが、コードを書きながら覚えていきましょう。

また、withletではブロックスコープを実現できました。

しかし、if文for文などにはブロックスコープは実現できませんでした。
このあたりは、他の言語と大きな違いですので、扱いには注意しましょう。

JavaScriptのスコープについて忘れてしまったら、またこのページをご覧ください!

この記事を書いた人

【プロフィール】
DX認定取得事業者に選定されている株式会社SAMURAIのマーケティング・コミュニケーション部が運営。「質の高いIT教育を、すべての人に」をミッションに、IT・プログラミングを学び始めた初学者の方に向け記事を執筆。
累計指導者数4万5,000名以上のプログラミングスクール「侍エンジニア」、累計登録者数1万8,000人以上のオンライン学習サービス「侍テラコヤ」で扱う教材開発のノウハウ、2013年の創業から運営で得た知見に基づき、記事の執筆だけでなく編集・監修も担当しています。
【専門分野】
IT/Web開発/AI・ロボット開発/インフラ開発/ゲーム開発/AI/Webデザイン

目次