スコープ 関数スコープ、ブロックスコープ [JavaScript]


JavaScriptにおけるスコープとは？

:::note info
変数が「参照できるか」？を現す有効範囲のことです。
:::

どこで変数を定義したかによって、どこからその変数を参照できるのかが決まります。
(関数の中？ 関数の外？ for文の中？ if文の中？)

そして、スコープにより参照できる範囲が限定されることで、
変数名の競合を避け、無駄なメモリの消費を抑える役割があります。


##  目次：

- [関数スコープ](#関数スコープ)
- [ブロックスコープ](#ブロックスコープ)

## 関数スコープ

関数の中のスコープ（有効範囲）についてみていきたいと思います。

#### 例： 関数の中のスコープ（有効範囲）について

collectという関数を定義します。

その中では、total = 10という数字を定義し、
それをconsole.logとして出力しています。

```js

function collect(){
    let total = 10;
    console.log(total);
}
```

```text: 出力結果
10
```

#### 問： このconsole.logを{}波括弧の下に書いたら正しく出力されるのか？

```js
function collect(){
    let total = 10;
}
console.log(total);
```

#### 出力結果
<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/b2be3412-700f-e673-74f9-72ca9fbdee03.png>

Errorが表示されてしまいました・・・

なぜでしょうか？

#### A.関数のスコープが影響しているからです。

このように、letやconstを使って変数を宣言した時、この変数は、その関数内にスコープ(参照できる範囲)が限定されるという性質を持ちます。

つまり、作成したcollect関数は、{}波括弧の中でしか使えず、関数の外では参照することができません。
なので、errorが表示されました。


### 関数のスコープの優先順位

また、関数のスコープには優先順位があります。
それぞれの例題をもとに、どの変数の値が優先されるのかをみていきましょう。


#### 例１： 関数は関係のない出力結果
関数の外でlet宣言をし、関数の外で出力結果を出す場合。

```js
let bird = "すずめ";

function birdWatch() {
    let bird = "カラス"
}
console.log(bird);
```
出力結果は関数の外で定義した値(すずめ)が出力される。
(関数は定義しただけで、実行していないので当然出力結果に関数は関係ない。)

```text:出力結果：
すずめ
```

#### 例２： 関数スコープの出力結果

関数の外でlet宣言をし、関数の外で出力結果を出す場合。関数の定義を関数内で、実行を関数の外側で行う。

```js
let bird = "すずめ";

function birdWatch() {
    let bird = "カラス"
}
birdWatch();
console.log(bird);
```
出力結果は関数の外で定義した値(すずめ)が出力される。
(関数スコープの概念上、関数の中で定義した値を外側で参照することができないため。）

```text:出力結果：
すずめ
```

#### 例３： let変数宣言の上書き
関数の外でlet宣言をし、関数の中で同じ変数名を新たに宣言する。また、関数の中で出力結果を出す場合。関数の定義を関数内で、実行を関数の外側で行う。

```js
let bird = "すずめ";

function birdWatch() {
    let bird = "カラス"
    console.log(bird);
}
birdWatch();
//console.log(bird);

```
出力結果は関数の中で定義した値(カラス)が出力される。
(関数スコープの概念上、関数の中で定義した値を外側で参照することができないため。外側のbirdWatchは実行されない。）

```text:出力結果：
カラス
```

#### 例4： スコープの拡張
関数の外でlet宣言をし、関数の中で出力結果を出す場合。関数の定義を関数内で、関数の実行を関数の外側で行う。

```js
let bird = "すずめ";

function birdWatch() {
    //let bird = "カラス"
    console.log(bird);
}
birdWatch();
//console.log(bird);

```
出力結果は関数の外で定義した値(すずめ)が出力される。

```text:出力結果：
すずめ
```

例３、例４の結果から分かる通り、関数の内側で同じ変数の宣言をした場合は、
関数の内側の変数が使用されるが、関数の内側の変数宣言がない場合、
外側の変数宣言が使用されます。

## ブロックスコープ

{ と }(波括弧)で囲んだ範囲をブロックと呼び、(スコープ)参照範囲を限定します。
ブロック内で宣言された変数は、スコープ内でのみ参照でき、スコープの外側からは参照できません。

#### 例1： ブロックスコープの外側のlet宣言
ブロックスコープの外でlet宣言をし、{}(波括弧)の中にif文、const宣言、let宣言をぞれぞれ記入。出力結果を{}(波括弧の)外側で行う。
```js
let circle = 10;
    if (circle > 5) {
    const PI = 3.14
    let msg = "綺麗な丸です。";
}
console.log(circle)
```
let宣言で宣言したcircleの値が出力される。
```text:出力結果：
10
```
#### 例2： ブロックスコープの内側のconst宣言
ブロックスコープの外でlet宣言をし、{}(波括弧)の中にif文、const宣言、let宣言をぞれぞれ記入。constの出力結果を{}(波括弧の)外側で行う。
```js
let circle = 10;
    if (circle > 5) {
    const PI = 3.14
    let msg = "綺麗な丸です。";
}
console.log(PI)
```
#### 出力結果：
ブロックスコープの性質上、内側の値を参照できないため、Errorが出力されます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/74dead31-c37c-608b-a56c-804324bbfc88.png>

#### 例3： ブロックスコープの内側のlet宣言
ブロックスコープの外でlet宣言をし、{}(波括弧)の中にif文、const宣言、let宣言をぞれぞれ記入。letの出力結果を{}(波括弧の)外側で行う。
```js
let circle = 10;
    if (circle > 5) {
    const PI = 3.14
    let msg = "綺麗な丸です。";
}
console.log(msg)
```
#### 出力結果：
ブロックスコープの性質上、内側の値を参照できないため、Errorが出力されます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/40fcadc6-f526-5636-d186-fccf429ba29e.png>

2番, 3番の例に関しては、どちらもブロックスコープの性質上{}(波括弧)の内側の値を参照できないため、errorが出力されます。これは、ブロックスコープの性質です。

#### 例4： if文だけではなく、for文もブロックスコープの対象。
for文を記入。letの出力結果を{}(波括弧の)外側で行う。
```js
for (let i = 0; i < 5; i++) {
    let msg = "iは５より小さいです。"
}
console.log(msg)
```
#### 出力結果：

for文も、if文同様、{}(波括弧)内の文章はブロックスコープの対象となり、外側で中を参照することはできません。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/c993a808-0cae-f2dc-c0cd-867d72c0d376.png>

ブロックスコープの性質上、内側の値を参照できないため、Errorが出力されます。


## まとめ：

:::note info 
スコープのまとめ：

・スコープとは、スコープは関数や{}（波括弧）などのアクセスする範囲を制限するものです。

・コードが変数にアクセスする際には、まずは内側から参照し、外側に参照先を広げていく性質を持ちます。