【JavaScript】スコープについて【まとめ】



# スコープってなに？


![demo](https://gyazo.com/dad9f50c767086013c6d717677794131/raw)

プログラミングにおける***Scope（スコープ）***とは***ある変数や関数が特定の名前で参照される範囲のことを言う***。

要は今実行中のコードからその変数や関数が見えるか見えないか（参照できるかできないか）ということ

# 実際に見てみる
↓このコードを実行すると

```javascript:main.js
{
  let a = 0;
  const b = function () {}
  debugger; //実行中のコード
}
```


![demo](https://gyazo.com/aa3ade64c82868f7956354a88e85c856/raw)
**変数a**と**関数b**が参照できているのがわかる
***

↓次にこのコードを実行すると

```javascript:main.js
{
  let a = 0;
  const b = function () {}
}
debugger; //実行中のコード
```
![demo](https://gyazo.com/4acd2a02b1fc1632a7d850804b325715/raw)

aとｂが参照できない・・・

つまり実行中のコード（今回はdebugger;）から変数や関数が見える範囲を***スコープ***という


#スコープの種類
JavaScriptには５種類のスコープが存在する。

- **グローバルスコープ**


- **スクリプトスコープ**
- **関数スコープ**
- **ブロックスコープ**
- **モジュールスコープ**

#グローバルスコープ
**スクリプトファイル直下**に`var`や`function`で定義するとここに入る

```javascript:main.js

var a = 0;
function b() {}
debugger; //実行中のコード

```
![スクリーンショット 2020-11-13 6.43.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/190d9865-82bd-4e15-8dcb-0f10ab69b761.png)

#スクリプトスコープ

**スクリプトファイル直下**に`let`や`const`で定義するとここに入る


```javascript:main.js

let a = 0;
const b = function() {}
debugger; //実行中のコード

```

![スクリーンショット 2020-11-13 6.58.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/e5aab068-107f-afbf-100d-87b08d0284e7.png)

**グローバルスコープ**と**スクリプトスコープ**はそのファイル内であればどこからでも参照可能。

#関数スコープ
名前の通り、関数の中のスコープ。

その関数内であれば変数ｂが参照できることがわかります。
![スクリーンショット 2020-11-13 7.27.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/688e032d-1256-d5aa-5ca4-01cd97f534ea.png)

一方関数の外では参照できません。

![スクリーンショット 2020-11-13 7.27.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/38c04ac4-4101-e294-7d9a-6ad67620a532.png)


#ブロックスコープ
**ブロックって？**
↓これです
![スクリーンショット 2020-11-13 7.34.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/ecf06d59-a0c6-50ca-1eef-de455ad7e577.png)
この`｛｝`の中をブロックスコープと言います。
記事の一番最初の例がブロックスコープの例となります。

**if文**や**for文**の中で定義された変数もブロックスコープとなります。
![スクリーンショット 2020-11-13 7.38.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/aaa1d9a6-5c01-ad87-b6f8-7f87ee55b7e0.png)
![スクリーンショット 2020-11-13 7.40.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/acd8b1fa-e6a9-3848-a474-79cfe935d773.png)

**もちろんこれらもブロック外からは変数を参照できません。**

# モジュールスコープ
モジュール（ESM）を使用した際に**スクリプトスコープ**が**モジュールスコープ**に切り替わる。
**inport / export** を使用しないと別ファイルの変数や関数を参照できない。

[【JavaScript】基本的なモジュール（ESM）の使い方についてまとめ](https://qiita.com/higa08/items/1112128192b1e0466cc6)


# レキシカルスコープ

これは**どのようにしてスコープが決定するかの仕様**のことです。

例として、まず**関数f1**を定義します。
その中に**変数a**を定義しています。

**ここで1つの関数スコープができました。**

```javascript:main.js

const f1 = () => {
  let a = 0;
}
f1();

```

さらに**関数f1**の中に**関数f2**を定義します。

**ここで関数スコープの中にもう1つの関数スコープができました**

**変数a**を出力させ、実行します。
結果として、**変数a**の値を取得し出力されます。



```javascript:main.js

const f1 = () => {
  let a = 0;
  const f2 = () => {
    console.log(a); // => 0
  }
  f2();
}
f1();
```

この時実行中の**関数f2**の**外側にあるスコープ**から変数を参照しています。
この外側にあるスコープを**外部スコープ（レキシカルスコープ）**と言います。

![スクリーンショット 2020-11-17 8.34.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/10882700-ea70-e1b0-d875-b3115bbce0df.png)


また、このようにスコープが複数階層になっている状態を**スコープチェーン**と言います。



# スコープチェーン

スコープが複数階層で、連なっている状態のこと。

スコープチェーンでは**一番内側のスコープから順番に変数を取得していく仕様**です。


```javascript:main.js

let a = 0;
const f1 = () => {
  let a = 1;
  const f2 = () => {
    let a = 2; //     <-これ
    console.log(a); // => 2
  }
  f2();
}
f1();
```


```javascript:main.js

let a = 0;
const f1 = () => {
  let a = 1; //      <-これ
  const f2 = () => {
    // let a = 2;
    console.log(a); // => 1
  }
  f2();
}
f1();
```

```javascript:main.js

let a = 0; //        <-これ
const f1 = () => {
  // let a = 1;
  const f2 = () => {
    // let a = 2;
    console.log(a); // => 0
  }
  f2();
}
f1();
```


<hr>

以上、スコープについてでした！
ここまで見ていただきありがとうございました。