なんとなくふわっと理解するJavaScriptのクロージャー

JavaScriptのスコープもES6からはかなり理解しやすくなってきたのですが、それでもわかるようでちゃんと理解してるか怪しいのがクロージャーです。

クロージャーの説明では定番の、呼び出されるたびに数字が増えていく関数を考えてみます。これは関数の外側でカウントを格納する変数を宣言して初期値をセットしておけば実現できますね。ただし、この方法だとどこからでもcountにアクセスできてしまうので事故りそうです。

let count = 0;
function counter() {
  return console.log(++count);
}

counter(); // 1
counter(); // 2
counter(); // 3

count = 'うんち';
counter(); // NaN

ではcountをグローバルに依存させない方法を考える必要がありますが、かといって関数内で宣言してしまうと関数を呼び出す度にリセットされてしまうため、カウンターが機能しなくなってしまいます。

このように変数を実行する関数の中には含みたくないが、グローバルにも依存させたくないという場面ではクロージャーが使えます。

function f() {
  let count = 0;
  function g() {
    return console.log(++count);
  }
  return g;
}

const counter = f(); // [Function: g]

counter(); // 1
counter(); // 2
counter(); // 3
count = 'うんち'; // (注)strictモードではエラーになります。
console.log(count); // うんち
counter(); // 4

JavaScriptの関数は定義されたときのスコープを保持します。つまり、counterに代入された関数gは宣言時のスコープ(関数f)内のcount変数にアクセスすることができます。ここまで実際に動かしてみるとMDNのクロージャーの説明はしっくりきます。

クロージャは、独立した (自由な) 変数を参照する関数です。言い換えるとクロージャ内で定義された関数は、自身が作成された環境を ‘覚えています’。

まだなんとなく理解したかも(専門用語で「完全に理解した」)の域を出ませんが、クロージャーが使えそうな場面では使ってみようと思います。