マッスル・メモリー

筋肉エンジニアのブログ

【js】クロージャ

クロージャとは、ローカル変数を参照している関数内関数のことです。

なんのこっちゃわからないので、次のコードを見てください。

function countPoint(init) {
  let point = init;

  return () => {
    return ++point;
  }
}

let myCounter = countPoint(1);

console.log(myCounter()); // 2
console.log(myCounter()); // 3
console.log(myCounter()); // 4
console.log(myCounter()); // 5
console.log(myCounter()); // 6

countPoint関数は戻り値は数値ではなく、数値をインクリメントするアロー関数を返しています。

引数や戻り値が関数である関数のことを高階関数と呼ぶのでした。

このように関数が戻り値として返されて起こることがクロージャの仕組みになります。

通常関数の中で使われたローカル変数(ここでいう変数point)は関数の処理が終了した時点で破棄されるはずです。

しかし、countPoint関数から返されたアロー関数がローカル変数のpointを参照し続けているので、

countPoint関数終了後もローカル変数pointは保持され続けるのです。

スコープチェーンで考えてみると、

  • アロー関数を表すCallオブジェクト
  • countPoint関数のCallオブジェクト
  • グローバルオブジェクト

というスコープチェーンがアロー関数が有効である間は保持されるのです。

別の例を見てみましょう。

function countPoint(init) {
  let point = init;

  return () => {
    return ++point;
  }
}

let myCounter1 = countPoint(1);
let myCounter2 = countPoint(100);

console.log(myCounter1()); // 2
console.log(myCounter2()); // 101
console.log(myCounter1()); // 3
console.log(myCounter2()); // 102

ここでわかるように、呼び出しごとに生成されたCallオブジェクトは別物なのです。

つまり、myCounter1とmyCounter2のCallオブジェクトは別物ですし、

そのCallオブジェクトに属するローカル変数pointもそれぞれ別物なのです。

クロージャとはに付け加えると、一種の記憶域を提供する仕組みでもあると言えます。

まとめ

クロージャとは、

  • ローカル変数を参照している関数内関数のこと
  • 一種の記憶域を提供する仕組みでもある