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のクロージャーの説明はしっくりきます。
クロージャは、独立した (自由な) 変数を参照する関数です。言い換えるとクロージャ内で定義された関数は、自身が作成された環境を '覚えています'。 クロージャ - JavaScript | MDN
まだなんとなく理解したかも(専門用語で「完全に理解した」)の域を出ませんが、クロージャーが使えそうな場面では使ってみようと思います。