【JS】やっと分かった、クロージャー。

#はじめに
【対象】
・クロージャーについて何度も調べて、実践しているが、なかなか理解できない人

・グローバル変数とローカル変数の違いはわかる人

・グローバル変数を出来るだけ使わないようにするのは分かるけど、やり方が分からない人

#そもそも、クロージャーとは？
>クロージャは、関数と、その関数が宣言されたレキシカル環境の組み合わせです。
[MDNより](https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures#:~:text=%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%A3%E3%81%AF%E3%80%81%E9%96%A2%E6%95%B0%E3%81%A8%E3%80%81%E3%81%9D%E3%81%AE,%E3%83%AC%E3%82%AD%E3%82%B7%E3%82%AB%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%AE%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E3%81%A7%E3%81%99%E3%80%82)

要は、

**「関数を囲むスコープにある変数を参照できる関数」**の事。

#クロージャーの特徴
①関数が呼ばれるたびに結果が変わる事
②グローバル変数を減らすことで、他のプログラムからの干渉を受けずらく出来る
**③処理回数が減らすことが出来る**（※重要）
④レキシカルスコープの言語で使う

クロージャーの特徴について説明する前に。

まずは、レキシカルスコープとダイナミックスコープの違いからの説明です。
（分かる人は飛ばしてください。）

#・レキシカルスコープとダイナミックスコープ
####①レキシカルスコープ
→関数を**定義した時点**でスコープが決まる。

↓コードで説明

```sample.js
var x = 10; 
function A(){
  console.log(x);
}
function B(){
  var x = 1000;
  A();
}
A();  // 10
B(); // 10
```

```function A(){}```として関数定義した時点で参照していたxが代入されている。

・メジャーな言語（JSなど）で採用される。

####②ダイナミックスコープ（Perl等の言語）
**実行時＞定義時**

→関数定義時にもスコープは決まるが、実行時のスコープ優先。

```sample.js
var x = 10; 
function A(){
  console.log(x);
}

function B(){
  var x = 1000;
  A();
}
A();  // 10
B(); // 1000
```

#クロージャーについて1からの説明
先程、記載した通り、

クロージャーとは、
**関数を囲むスコープにある変数を参照できる関数**の事です。

コードで説明すると、

```sample.js
function outer() {
  let place = '大阪';
  console.log(place);

  function inner() {
    alert(place);
  }
  return inner;
}

let Func = outer();

Func();
```

上から順に説明していきます。
（基本的な所まですべて記述します）

・```let place```：```outer(){}```のローカル変数。（※```outer()```実行時以外は参照できない）

・```console.log(place);```：```outer();```で実行される

・```function inner() {}```：これが、**クロージャー**です。

・```alert(place);```：```Func();```で実行される。

・```let Func = outer();```：ここで、```outer();```が実行され、また、```inner()```がクロージャーとなって、代入されている。

ここで分かった、クロージャーの性質は、

**outer()が実行されていないのに、outer()内の変数placeが参照される**事です。

###【上記コード：ここまでの流れのまとめ】
```let Func = outer();```と```Func();```で関数の実行を行っている
↓
```let Func = outer();```の定義でのみ、```outer()```が実行され、```console.log(place)```が実行される
↓
```let Func = outer();```の定義で```inner()```がクロージャーとして代入される
↓
```Func();```で```inner()```の中身を実行

#【特徴①】関数が呼ばれるたびに結果が変わる事について

～【今回行う事】～
関数を呼び出すたびに、数が増えていく処理を行います。

通常の関数では？

####【通常】

```sample.js
function outer() {
    let count = 1;
 
    count++;
    console.log(count);
}

outer(); // 2
outer(); // 2
outer(); // 2
```

上記のコードでは、```outer();```を何度実行しても呼び出されるたびにcountには```1```が代入されるため同じ結果しか得れません。

####【クロージャー使用】

```sample.js
function outer() {
  let count = 1;
  console.log(count);

  function inner() {
    count++;
    console.log(count);
  }
  return inner;
}

let sum = outer(); // 1

sum(); // 2
sum(); // 3
sum(); // 4
```

実現できました。

#【特徴②】グローバル変数を減らすことで、他のプログラムからの干渉を受けずらく出来るについて

【特徴①】のコードと数字の増えるプログラムで説明すると、

下のようなコードでも動くのでは？

```sample.js
let count = 1;
console.log(count);

function outer() {
    count++;
    console.log(count); // 1
}

outer(); // 2
outer(); // 3
outer(); // 4
```

```let count = 1;```を関数の外で定義しています。

グローバル変数として定義した場合も、思い通りの処理が完成しました！

しかし、

これでは、クロージャーを勉強している意味はありません。

そもそも、クロージャーを使う目的の一つとして

**グローバル変数を使わないようにすることで、他のプログラムからの干渉を受けずらく**という事があげられます。

**グローバル変数を使うことで、変数名が被った際に参照先の違いで思わぬエラーが発生する可能性もあります。**

上記のコードでは、グローバル変数```count```にアクセスしています

それをしないために、クロージャーを使ってカプセル化を行います。

####【クロージャー使用】

```sample.js
function outer() {
  let count = 1;
  console.log(count);

  function inner() {
    count++;
    console.log(count);
  }
  return inner;
}

let sum = outer(); // 1

sum(); // 2
sum(); // 3
sum(); // 4
```

何となくメリットが分かってきたような？？

#【特徴③】処理回数を減らす
→【特徴】の部分でも記載した通り、クロージャーを使うことで、処理回数を減らすことにもつながります。

「クロージャーについて1からの説明」にあるコードを少し変えたのコードで説明します。

####【クロージャーなし】
```sample.js
function outer() {
  let place = '大阪';
  let word;
  // console.log(place);
  
  if(place === '大阪') {
    word = 'せやな！';
  } else {
    word = 'ちゃうやん';
  }
  
  window.alert(word)
}

outer();
```

上記のコードだと、```outer();```が呼び出されるたびに毎度ifで処理しないといけません。

しかし、、、

####【クロージャーを使うと？】

```sample.js
function outer() {
  let place = '大阪';
  let word;
  // console.log(place);
  
  if(place === '大阪') {
    word = 'せやな！';
  } else {
    word = 'ちゃうやん';
  }

  function inner() {
    window.alert(word)
  }
  return inner;
}

let Func = outer();

Func();
```

```let Func = outer();```で一度if分の条件分岐の処理を行った後は、```Func();```を何度実行しても呼び出されるのは、```inner()```内部だけです。

見事に、**クロージャーの使用で処理回数を減らすことを実現できました！**

#【おまけ】これは無理なん？と思ったもの

##①returnではなく、関数定義内で関数実行

```sample.js
function outer() {
  let count = 1;
  console.log(count); // 1

  function inner() {
    count++;
    console.log(count);
  }
  
  inner(); // 2
}

outer(); // 1 2
outer(); // 1 2
```

```
1
2
1
2
```

上記のコードでは、```outer()```実行時に```outer()```内で```inner()```が実行されてしまっています。

関数を実行するたびに```count = 1```に```count++;```しているだけです。

##②少し書き方をかえると？
```sample.js
function outer() {
  let count = 1;
  console.log(count);

  return function inner() {
    count++;
    console.log(count);
  }
}

let sum = outer(); // 1

sum(); // 2
sum(); // 3
```

```
1
2
3
```

何が変わったか？・・・```return```を移動させて、```function inner()```の前に付加しただけです。

勿論、変わらず動きます。

続いてこちら、

```sample.js
let outer = (function() {
  let count = 1;
  console.log(count);

  return function inner() {
    count++;
    console.log(count);
  }
})();

outer(); // 2
outer(); // 3
```

9行目の最後の```()```で```outer```を実行。

その後、```outer()```で```inner()```を実行しています。

こちらも、```outer()```と```inner()``で切り離して実行できます。

#まとめ
クロージャーを使うことで、

・**グローバル変数を減らすことで、他のプログラムからの干渉を受けずらく出来る**

**・処理回数が減らすことが出来る**

#参考
[【JavaScriptの基礎】レキシカルスコープとクロージャを理解する](https://wemo.tech/904)

[私が今までクロージャを理解できなかった理由](https://artgear.hatenablog.com/entry/20120115/1326635158)

[【JavaScript入門】クロージャって一体何？使い方まで徹底解説](https://www.sejuku.net/blog/25026)