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

 __クロージャは、JavaScriptでは非常に重要な概念の一つですが、初心者にとっては抽象的で難解に感じることがあります。特に、ECMA規格での定義は実務経験がないと理解しにくいでしょう。そこで、本記事ではクロージャの概念について長々と説明するのではなく、具体的なコードを通して短時間で理解できるようにします。__

## 1. これがクロージャだ

新しい技術に触れる際に、まず最初にやるべきことは、そのデモコードを探すことです。私たちにとって、コードを読むことは自然言語よりも物事の本質を理解しやすいです。実際、クロージャは至るところで見られます。たとえば、__jQuery__ や __Zepto__ のコアコードは大きなクロージャに包まれています。では、まず最も簡単で基本的なクロージャを紹介します。これにより、クロージャの概念を頭の中にイメージしやすくなるでしょう。

```javascript
function A(name){
    function B(){
       console.log(name);
    }
    return B;
}
var C = A("田所浩二");
C();//田所浩二
```

これは最も簡単なクロージャです。

これで基本的な理解ができたところで、通常の関数と何が異なるのかを簡単に分析してみましょう。上記のコードを自然言語に翻訳すると、以下のようになります。

1. 通常の関数Aを定義する、引数はname
2. A の中で通常の関数 B を定義する、関数Bでは外部変数nameを参照する
3. A の中で B を返す
4. A を実行し、その結果を変数 C に代入する
5. C を実行する

この5つの操作を一言でまとめると、次のようになります。

**関数Aの内部関数Bおよび変数nameが、関数Aの外部で変数Cによって参照されている。**

この文を少し加工すると、次のようにクロージャの定義となります。

**ある内部関数が、その外部関数の外にある変数によって参照されると、クロージャが形成される。**

したがって、上記の5つの操作を行うと、クロージャが定義されるということになります。

これがクロージャだ。

## 2. クロージャの用途

クロージャの用途を理解する前に、JavaScriptのGC（ガーベジコレクション）メカニズムについて理解しましょう。

**JavaScriptでは、オブジェクトが参照されなくなると、GCによって回収されます。そうでない場合、そのオブジェクトはメモリに保持され続けます。**

上記の例では、B は A の中で定義されているため、B は A に依存しています。そして、外部変数 C が B を参照しているため、A は間接的に C によって参照されていることになります。

つまり、A は GC によって回収されず、メモリに保持され続けます。この推論を証明するために、上記の例を少し改良してみましょう。

```javascript
function A(){
    var count = 0;
    function B(){
       count ++;
       console.log(count);
    }
    return B;
}
var C = A();
C();// 1
C();// 2
C();// 3
```

#### コード説明

1. `var C = A();` と呼び出すと、`A` が実行され、`count` 変数と内部関数 `B` が作成されます。`A` は `B` を返すので、`C` 変数は実際には `B` の参照を持ちます。

2. 関数 `B` は `A` 内の `count` 変数にアクセスできます。なぜなら、`B` はクロージャであり、クロージャはその作成時のコンテキスト（ローカル変数など）を保持するからです。

3. `C()` を呼び出すと、実際には `B` 関数が呼び出されます。`C()` を呼ぶたびに、`B` は `count` の値を増加させ、その値をコンソールに表示します。`B` はまだ `count` 変数を参照しているので、`count` の値は増え続けます。

4. `A` の実行コンテキストは `B` が作成された時点で終了しますが、`B` がそのローカル変数（`count` など）を参照している限り、`A` のローカル変数は回収されません。

5. `B` が参照されなくなった時点で初めて、`count` 変数や `A` 内の他のローカル変数が回収されます。この例では、`C` がまだ `B` を参照しているため、`count` の値は回収されず、`A` の実行コンテキストも回収されません。

#### なぜ `count` はリセットされないのか

- **クロージャのメカニズム**：クロージャによって `count` の状態が保持され、内部関数 `B` からアクセス可能な状態が維持されます。たとえ `A` の実行コンテキストが終了しても、`B` がこの状態を参照し続けるため、`count` の状態はメモリに残ります。
- **毎回 `B` を呼び出す際**：毎回 `C()` を呼び出すことは実際には `B()` を呼び出すことであり、`B()` はクロージャに保存されている `count` を使用し、再初期化することはありません。

したがって、モジュール内でいくつかの変数を定義し、これらの変数をメモリに保持しつつ、グローバルな変数を「汚染」しないようにしたい場合、クロージャを使ってこのモジュールを定義することができます。

## 3 LeetCodeでの実践

クロージャの理解を確認するために、LeetCodeの練習問題を通じて実践してみましょう！

リンク：https://leetcode.com/problems/counter/description/?envType=study-plan-v2&envId=30-days-of-javascript

```
//日本語翻訳

2620. カウンター
簡単
企業
ヒント
整数 n が与えられたとき、カウンター関数を返します。このカウンター関数は、最初は n を返し、その後は呼び出されるたびに前回の値より 1 多い値を返します（n, n + 1, n + 2, など）。

例 1:

入力: 
n = 10 
["call","call","call"]
出力: [10,11,12]
説明: 
counter() = 10 // 最初に counter() が呼び出されたとき、n を返します。
counter() = 11 // 前回より 1 多い値を返します。
counter() = 12 // 前回より 1 多い値を返します。
例 2:

入力: 
n = -2
["call","call","call","call","call"]
出力: [-2,-1,0,1,2]
説明: counter() は最初に -2 を返し、その後の呼び出しごとに値が増加します。
 
制約:

-1000 <= n <= 1000
0 <= calls.length <= 1000
calls[i] === "call"
```

```javascript
//デフォルトコードテンプレート
/**
 * @param {number} n
 * @return {Function} counter
 */
var createCounter = function(n) {
    
    return function() {
        
    };
};

/** 
 * const counter = createCounter(10)
 * counter() // 10
 * counter() // 11
 * counter() // 12
 */
```
##### ここまで来れば、皆さんはクロージャの基本的な概念を把握していることでしょう。

##### 当該問題の解法や回答については、コメント欄にご意見をお寄せください！
##### ( •̀ ω •́ )y