【図解】コールスタックとクロージャーを理解する

# スコープ

ある変数の**スコープ**とは、その変数にアクセスできるコード上の範囲を意味する。

`var` で宣言された変数は **function scope** であるのに対し、`let` や `const` で宣言された変数は **block scope** と呼ばれるスコープを持っている。

function scope の変数は、**自分を含む一番内側の関数**がスコープとなる。一方 block scope の変数は、**自分が属する一番内側の関数orブロック**がスコープとなる。ブロックとは `{ 処理;処理;... }` のことである。

![var let scope](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/28112eef-4dc8-f901-3f0f-f12f8697fc76.png)


# Lexical Environment (LE)

JavaScript の実行環境はプログラムを実行する上で「どの変数が何の値を持っているか」というテーブルのようなものを保持しながらコードを一行一行実行する。ただし、変数はスコープというものがあり、スコープの外からはアクセスすることはできない。また、変数を参照するとき、同じ名前の変数が複数あればより内側のスコープに存在するものが優先される(シャドウイング)。また、後で説明するがJavaScriptの関数はクロージャーという機能を持っている。

このような性質を実現するために、JavaScript ではテーブルのチェーン構造によって変数を管理している。このテーブル一つ一つを **Lexical Environment** と呼び、変数の名前と値のペアを格納する。

[仕様での定義](https://www.ecma-international.org/ecma-262/10.0/index.html#sec-lexical-environments)

# Execution Context Stack

Call Stack とも呼ばれるが、これは「今実行中の関数はどの関数から呼び出されて、その関数はどの関数から呼び出されて...」という履歴のようなものを格納する[スタック構造](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF)である。そしてこのスタックに詰め込まれるのは Lexical Environment である[^envcontext]。

[^envcontext]: 仕様では Execution Context Stack に詰め込まれるのは Execution Context であるが、ここでは Execution Context と Lexical Environment を同一視している。

[仕様での定義](https://www.ecma-international.org/ecma-262/10.0/index.html#sec-execution-contexts)

# 実行の流れ

例えば以下のコードを考えよう。

```js
const x = 1

if (true) {
  const y = x + 1
  const z = f(y)
}

function f(x) {
  return x * 2
}
```

まずは、トップレベル(一番外側のスコープ)の変数のための LE が作られる。画像右の白い四角が LE を表す。この時点では通常の変数に値は入っていない(初期化されていない)。しかし、関数宣言は hoisting (巻き上げ) が起こるので、既に変数 `f` の値(関数)は入っている。

![context1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/521612ea-fc18-80b7-6cce-e6cb07c4b95a.png)

1行目の右辺が計算され、`x` に代入される。

![context2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/1b48121e-d0e8-447a-ccbb-f2b0957c6245.png)

`if` の条件式が `true` なので**ブロックの中に入り、新しい LE が作られる**。

![context3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/4de1e720-e155-44ad-0bc4-3b14860223a7.png)

`y` の値が計算される。

![context4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/bf88932e-fd3e-9e57-201a-05bfdd140198.png)


次に、`f` 関数が呼ばれる。**関数の中に入るときも LE が作られる**。

**緑の LE が 青の LE ではなく赤の LE につながっている**が、これは関数 `f` の外側のスコープが赤の LE だからである。

実引数として渡した `y` の値 `2` が、`f` の仮引数 `x` の値となる。


![context5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/84aaa5a5-7ed3-23e2-bbda-d05a2c52e940.png)


`x * 2` を計算するときに `x` が参照されるが、このとき**赤の `x` ではなく緑の `x` の値が使われる**ことになる。どのように `x` を見つけているかというと、**Call Stack の先頭(図では一番下)の LE からスタートして、矢印でつながったチェーンをたどって一番最初に見つかった変数が選ばれる**のである。そのため、緑の LE をまず探して、そこになければ赤の LE を探すことになるが、今の場合緑の LE に `x` があるのでそれが選ばれる。変数のシャドーイングはこのようにして発生するのである。

よって `x` の値は `2` なので `4` が `return` される。

![context6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/765b4e88-cc1f-8773-a3be-52f337c7222a.png)

関数を抜け、緑の LE は Execution Stack から削除される。

戻り値 `4` が `z` に代入される。

![context7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/4ee38e41-56ab-6fbb-0115-75e0e8faff02.png)

ブロックを抜け、赤の LE は Execution Stack から削除され、このまま実行が終了する。

![context8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/4a2c5226-3e9d-91ce-6d28-621216a20a9e.png)

# クロージャー

クロージャーとは**外側のスコープに存在する変数を参照する関数**のことである。例えば下のコードでは `increment` 関数がクロージャーである。

```js
const c1 = counter()
const c2 = counter()

console.log(c1())
console.log(c1())
console.log(c1())

console.log(c2())
console.log(c2())

function counter() {
  let x = 0

  function increment() {
    x += 1
    return x
  }

  return increment
}
```
実行すると以下が出力される。

```
1
2
3
1
2
```

`counter` 関数を呼び出すたびに、別々のカウンターが作成されていることが分かるだろう。つまり、`c1` と `c2` は別々の変数 `x` を持っている。これも、LE で説明することができる。

`c1` と `c2` が代入された時点では、LE と Call Stack は次のようになっている。`c1` と `c2` はどちらも `increment` 関数の関数オブジェクトだが、**別々の LE(緑) を指している**ことが分かる。これは、関数が呼び出されるたびに LE が作られるからである。

![closure1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/2ff07f59-9451-28f2-4cc7-b06c3b0115f1.png)

`c1` が実行されると、**その関数オブジェクトが指している LE (左の緑の LE) の下に新しい LE が作られ**、そこで `increment` 関数が実行される。

![closure2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/446383/8b4bb1f9-b5e3-c46a-f8d9-ff8524c877a8.png)

ここで `x += 1` が実行されるわけだが、最初の例で説明したとおり、当然この `x` は左の緑の LE の `x` を指すことになる。これにより左の緑の LE の `x` が `1` になり、右の緑の LE は影響を受けない。つまり、独立した2つの状態変数となっている。

ところで、この2つの `x` は対応する `increment` 関数からしかアクセスできない、いわば「隠れた変数」となっている。最近になって JavaScript にプライベートメンバー変数の構文が追加されたが、それまではプライベート変数はクロージャーでしか実現できなかった。