未経験からエンジニアになりたい人必見!
おすすめプログラミングスクール3選!

【JavaScript】クロージャについて理解しよう

本記事では、JavaScriptの登竜門と呼ばれているクロージャについてサンプルコードを用いて分かりやすく解説しています。

JavaScriptの学習におすすめ参考書
改訂新版JavaScript本格入門

参考書が苦手な人はUdemyの動画がおすすめ
ガチで学びたい人のためのJavaScriptメカニズム

作成者 CodeMafia
学習時間 20.5時間
受講者数 16,593人
レビュー (2,383件)

クロージャとは?

クロージャとは、スコープ内に宣言された変数を関数が使用している状態のことを指します。

言葉だけだと何を言いたいのかサッパリわからないと思うので、とりあえずスコープ内の変数を関数が使用ことと頭の片隅に覚えて、実際にクロージャーを使うとどのようなことができるようになるのかサンプルコードを用いて解説していきます。

プライベート変数の生成

クロージャを使うとできることの一つ目は、値を保持し続けられるプライベート変数を作成することができます。プライベート変数とは外部から参照することができない変数のことを指します。

例えば、関数が呼び出されると1ずつカウントされていく処理を作成する場合、クロージャを利用しないと以下のような処理になります。

JavaScript
  function incrementFunc() {
    let val = 0;
    val++;
    console.log(val);
  }

  for (let i = 0; i < 10; i++) {
    incrementFunc();
  }

この処理は一見問題なく実行されそうですが、コンソールに出力される値は1のみです。これは何故かというと、関数内で宣言された変数はその関数が再度呼び出されると初期値の0を代入しリセットするからです。

では、変数を関数内ではなく関数外に設定すれば良いのでは?と思いますが、この場合コンソールに出力される値は1〜10で問題ありませんが、関数外に設定される変数はグローバル変数のためどこからで参照・更新することができてしまいます。

このように、プライベート変数に値を保持し続けたい時にクロージャを使用します。クロージャを使った場合の記述は以下の通りです。

JavaScript
<script>
  function sampleFunc() {
    let val = 0;
    function increment() {
      val++;
      console.log(val);
    }
    return increment;
  }

  const increment = sampleFunc();
  for (let i = 0; i < 10; i++) {
    increment();
  }
</script>

ここで重要となるポイントは「変数を宣言する場所」「return increment」の2つです。別々に解説していきます。

変数を宣言する場所

1つ目の注目ポイントは、変数を宣言する場所です。今回変数を宣言した場所は一番外側の関数sampleFunc()内です。

sampleFunc()内に変数を設定することでその変数はその関数内でしか使えない、つまりプライベート変数になります。increment関数はsampleFunc関数内にあるので、変数を参照することができます。

JavaScript
  function sampleFunc() {
    let val = 0; // -> OK
    function increment() {
      let val = 0 // -> NG
      val++; // -> 参照可能!
      console.log(val);
    }
    return increment;
  }

returnで関数を返す

2つ目の注目ポイントは、returnで関数を返すことです。このreturnで変数を返してあげることで、sampleFunc関数が呼ばれてもincrement関数が実行されるようになります。

ここで1つ前の変数だけをincrement関数外に宣言した意味が分かります。

returnでincrement関数を返却すると、sampleFunc関数が呼ばれても処理が実行されるのはincrement関数のみ = increment関数外で宣言された変数は関数が実行されるたびに初期化されない。という仕組みになります。

JavaScript
  function sampleFunc() {
    let val = 0; // -> 最初に宣言されて以降、処理は実行されない
    function increment() { // -> sampleFunc関数が呼び出されたらこの関数だけが実行される
      val++;
      console.log(val);
    }
    return increment; // -> increment関数を返却する
  }