JavaScriptのスコープについて

#スコープ
実行中のコードから値と式が参照できる範囲のこと。
JavaScriptでは次の５つのスコープが存在する。
１．　グローバルスコープ
２．　スクリプトスコープ
３．　関数ルスコープ
４．　ブロックスコープ
５．　モジュールスコープ

###グローバルスコープとスクリプトスコープ
まず下記のコードの挙動を開発ツールで確認してみます。

```JS:JS
let a = 0;
var b = 0;
function c() {};
debugger
```

![スクリーンショット 2021-10-16 20.53.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/ca33366a-e1db-18bb-5560-2be09cbd8b42.png)

Scopeの中にはScriptとGlobalが確認できます。
Scriptの中にはletで定義したaの値、Globalの中にはvarで定義したbおよび関数cが確認できます。このようにlet,constで宣言したものはスクリプトスコープ、varや関数はグローバルスコープに属します。ただ、一般的にはスクリプトスコープもグローバルスコープと呼ばれます。
グローバルオブジェクトについてもう少し詳しく見てみましょう。

先どの開発ツールからグローバルオブジェクトにbと関数cが格納されていることは確認できました。一般的にbの値を取得したい時は
の↓のような書き方はしません。

```JS:JS
let a = 0;
var b = 0;
function c() {};

console.log(window.b);
```

一般的な取得

```JS:JS
let a = 0;
var b = 0;
function c() {};

console.log(b);
```
ここからわかるようにグローバルオブジェクトは省略して書くことができます。（※console.log(window.b)でもbの値は取得できます）
したがってJavaScriptではwindowオブジェクトそれ自体がグローバルスコープという事ができます。

###関数スコープとブロックスコープ
関数スコープはfunction(){}の{}の中のスコープを意味します。

```JS:JS
function a() {
 let b = 0;
 console.log(b);
};

a();
```
![スクリーンショット 2021-10-16 21.09.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/866b48b8-4a34-06e8-9640-fba0836cd96c.png)

関数スコープ内で宣言したbの値を取得できています。
次のように書き換えるとどうなるでしょうか。

```JS:JS
function a() {
 let b = 0;
};

console.log(b);
a();
```

![スクリーンショット 2021-10-16 21.12.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/6331d487-6ed0-4d81-c66c-157ce32fd134.png)

エラーが発生してしまいます。スコープは実行中のコードから値と式が参照できる範囲のことなので、関数スコープで宣言された変数はスコープの外からは参照できないということになります。

次にブロックスコープについて確認します。
まず、ブロックスコープ内に変数宣言しconsoleで出力します。

```JS:JS
{
  let a = 1;
  console.log(a);
}
```
![スクリーンショット 2021-10-16 21.32.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/1aaa7e86-b3ce-39b0-4f56-ac1d3e97a64f.png)

次にブロックスコープの外で出力してみます。

```JS:JS
{
  let a = 1;
}

console.log(a);
```
![スクリーンショット 2021-10-16 21.34.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/cd6ea1da-1c11-0c0f-3d17-e5075856d117.png)

このエラーはスコープの外からは参照できないからです。
ここで変数宣言をvarで行ってみましょう。

```JS:JS
{
  var a = 1;
}

console.log(a);
```

![スクリーンショット 2021-10-16 21.32.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/b534abb2-3be3-704a-09ba-4f057de256ed.png)

varを使うとブロックスコープが無視されてしまします。したがってブロックスコープ内での変数の宣言はlet,constで宣言する必要があります。また関数宣言もブロックスコープを無視してしまう特性があるのでブロックスコープ内での関数宣言は下記のように一度変数に格納する必要があります。

```JS:JS
{
  const b= function (){
    console.log("b is called");
  }
}
b()
```

この場合は一度constに格納しているのでブロックスコープの外から参照することはできないのでエラーが発生します。今回は単純なブロックで説明しましたが、本来はif文やfor文で用いられます。

#スコープチェーン
スコープチェーンとはスコープが複数階層で、連なっている状態のことを指します。

```JS:JS
let a = 1;
function fn1() {
  let b =1;
  function fn2() {
    let c = 3;
  }
  fn2();
}
fn1();
```
![スクリーンショット 2021-10-17 13.16.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1265227/1d34d1f8-e38b-89c8-71e3-9d678b12e8de.png)

まず、fn2の関数はレキシカルスコープである変数b,aが参照可能となります。
仮に下記のコードに書き換えた時コンソールではどの値が取得されるでしょうか。

```JS:JS
let a = 1;
function fn1() {
  let a =1;
  function fn2() {
    let a = 3;
    console.log(a);
  }
  fn2();
}
fn1();
```

答えは3になります。イラストでも確認できるようにスコープチェーンは実行スコープに近いものから順に値を探しにいくからです。


##参考
Udemy： 【JS】ガチで学びたい人のためのJavaScriptメカニズム