クロージャは、組み合わされた(囲まれた)関数と、その周囲の状態(レキシカル環境)への参照の組み合わせです。言い換えれば、クロージャは関数にその外側のスコープにアクセスする機能を提供します。JavaScript では、クロージャは関数が作成されるたびに、関数作成時点で作成されます。
by MDN
難しい...😇
はっきり言って意味がわからない
ということでクロージャについて、自分の言葉で説明してみました。
✅ レキシカルスコープを理解する
クロージャを理解する前に、「レキシカルスコープ」という概念を理解する必要があります。
💡レキシカルスコープとは?
『関数が参照できるスコープは、その関数が定義されたときに決まる』 という、参照の仕様のことです。
Q:コンソールには何が表示されるでしょうか?
let count = 0; // ・・・①
function showCount1() {
console.log(count);
}
function showCount2() {
let count = 100; // ・・・②
showCount1();
}
showCount2();
A:答えは「0」です
0
解説
- JavaScriptでは、関数が参照できるスコープは、それらが定義された時に決まる(=レキシカルスコープ)
-
showCount1
関数が定義された時、①で定義されたcount
を参照している
結果、コンソールには「0」と表示されます。
JavaScriptでは関数を定義したときに参照できるスコープが決まることを抑えておきましょう👍🏻
✅ クロージャを理解する
レキシカルスコープを理解したところで、次はそれを利用した「クロージャ」について理解していきましょう。
💡 クロージャとは?
クロージャとは、関数とその定義時のレキシカルスコープを一緒に保持し
関数の外からでもそれらを参照することができる仕組み のことです。
この仕組みによって、クロージャは「状態の保持」や「変数のカプセル化」などの用途で活用されます。
👀 カプセル化の例でクロージャをみてみましょう
Q:コンソールには何が表示されるでしょうか?
function createCounter() {
let count = 0; // ・・・①
function countUp () {
count += 1;
console.log(count);
};
return countUp; // ・・・② countUp変数の定義を返す
}
const counter = createCounter(); // ・・・③ クロージャを生成
counter();
counter();
counter();
A:答えは「1 2 3」です
1
2
3
解説
-
countUp
関数の内部で①のcount
を参照しています -
createCounter
関数を実行すると、クロージャが生成され、定義された変数(ここではcount
)とそのスコープがセットで返されます ・・・②
その結果、counter()
を実行するたびに、記憶されたcount
の値が更新されて保持され続けるため、コンソールには「1 2 3」と順に表示されます。
カプセル化された値は、クロージャによって保持されている
当たり前ですが、createCounter
関数の外側でcount
に参照しようと思ってもundefined
になってしまいます。
function createCounter() {
let count = 0;
function countUp () {
count += 1;
console.log(count);
};
return countUp;
}
const counter = createCounter();
console.log(count) // undefined;
これは、count
がcreateCounter
関数のスコープ内に閉じ込められてるためです。(=カプセル化)
countUp
関数がcount
を参照している限り、クロージャはcount
の値を保持するため、関数の外からでもクロージャを通して参照することができます。
これにより、関数がcount
を保持しているように振る舞います。(=変数の保持)
まとめ
- JavaScriptでは関数が定義された時点で参照できるスコープ(=レキシカルスコープ)が決まる
- クロージャとは、その関数とスコープ情報をセットで記憶する仕組み
- その結果、関数が状態を保持したり、外からアクセスできない変数を守ったりできるようになる