うわー、あなたは完全に混乱していませんか。クロージャについてできるだけ簡単に説明します。
まず、スコープから始めます。スコープには次の 2 種類があります。
- ブロックスコープ
- 関数スコープ
ブロック スコープは、プログラム内で発生するとすぐに開始されます。一方、関数スコープは、関数が呼び出されるまで開始されません。したがって、同じ関数を複数回呼び出すと、複数のスコープが作成されます。
JavaScript にはブロック スコープがありません。関数スコープしかありません。したがって、ブロックスコープをエミュレートするには、関数式を作成してすぐに実行する必要があります。このパターンは即時呼び出し関数式 (IIFE)と呼ばれ、次のようになります。
(function () {
// this is the JS equivalent of a block scope
}());
ブロック スコープと関数スコープの他に、スコープを分類する別の方法があります。したがって、次のものもあります。
- レキシカルスコープ
- 動的スコープ
ブロックスコープは常にレキシカルスコープであるため、この区別は関数スコープにのみ適用されます。JavaScript には字句スコープしかありません。
レキシカルスコープと動的スコープの違いを理解するには、自由変数と束縛変数の違いを理解する必要があります。
- 自由変数は、関数内で使用されるが、その関数内で宣言されていない変数です。
- 関数内で宣言された変数は、その関数にバインドされていると言われます。
次のプログラムを検討してください。
function add(x, y) {
return x + y; // x and y are bound to add
}
上記のプログラムでは、変数x
とy
は 内で宣言されているため、関数 add にバインドされていますadd
。
一方、次のプログラムの変数x
とは、関数内で宣言されていませんが、関数内で使用されているため、関数内では自由です。y
add
add
add
function add() {
return x + y; // x and y are free within add
}
今、自由変数が問題です。それらは何らかの値にマップする必要がありますが、どの値ですか? ここでレキシカル スコープとダイナミック スコープの出番です。主要な詳細については説明しませんが、ウィキペディアで読むことができます。
スコープは、プロトタイプの継承によく似ています。新しいスコープが始まると、JavaScript のプロトタイプ チェーンのように、スコープのチェーンを形成する親スコープから継承されます。
レキシカルスコープと動的スコープは、新しいスコープがフォームを継承する親スコープに関して異なります。
- レキシカルスコープでは、新しい関数スコープは、その関数が定義されたスコープ (つまり、そのレキシカル環境) から継承します。
- 動的スコープでは、新しい関数スコープは、その関数が呼び出されたスコープ (つまり、呼び出しスコープ) から継承します。
JavaScript にはレキシカル スコープしかないので、動的スコープは気にしません。次のプログラムを検討してください。
var count = 0;
function incrementCount() {
return ++count;
}
(function () {
var count = 100;
alert(incrementCount()); // 1
}());
ここで、関数incrementCounter
には 1 つの自由変数 - がありcount
ます。JavaScript にはレキシカル スコープがあるため、IIFE 内で宣言されたローカル変数ではなくcount
、グローバル変数にマップされます。したがって、 を返し、 を返しません。count
count
incrementCount
1
101
クロージャーは、レキシカル スコープを持つ言語でのみ機能するようになりました。次のプログラムを検討してください。
function getCounter() {
var count = 0;
return function () {
return ++count;
};
}
var counter = getCounter();
alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3
上記のプログラムでは、によって返される関数は、次の理由getCounter
により、変数に関するクロージャです。count
- 変数
count
は、返された関数 (つまりcounter
) 内で解放されます。
- 関数は、
count
宣言されているスコープの外に移動されます。
関数がクロージャーと呼ばれるには、これらの両方の条件が必要です。詳細については、次の回答をお読みください: https://stackoverflow.com/a/12931785/783743
ここで理解しておくべき重要なことは、関数counter
が呼び出されなくても、クロージャーとして呼び出されるということです。クロージャは、変数 (クロージャの上位値と呼ばれます)を閉じる関数です。
呼び出すときにgetCounter
新しいスコープを作成し (このスコープを呼び出しましょう)、 (つまり)A
によって返される関数を呼び出すたびに、scope から継承する新しいスコープを作成します。それで全部です。新しい閉鎖は作成されません。getCounter
counter
A