JavaScript のクロージャとは何ですか?
JavaScriptクロージャは、それが作成された環境を記憶する関数です。これは、1 つのメソッドとプライベート変数を持つオブジェクトと考えることができます。
JavaScriptクロージャは、クロージャが作成されたときに、関数と関数のローカルスコープとすべての変数(環境)を含む特別な種類のオブジェクトです。

クロージャを理解するには、まずJavaScriptのSCOPINGを理解する必要があります。 変数または関数は、3 つのレベルのスコーピングで作成できます。
- Global scope
- 関数またはローカルスコープ
- 字句スコープ
Scopes in JavaScript
変数を作成するとすぐに、それはグローバルスコープになります。したがって、どの関数にも存在しない変数を作成した場合、それはグローバルスコープ内にあります。
var foo = "foo"; console.log(foo);
何かがグローバルな範囲にある場合、私たちはどこからでもアクセスできるため、それは私たちの味方であると同時に敵でもあります。すべてをグローバルスコープに入れることは、他の問題の中で名前空間の競合を引き起こす可能性があるため、決して良い考えではありません。何かがグローバルスコープにある場合、どこからでもアクセスでき、関数に同じ名前の変数がある場合、競合が発生する可能性があります。
グローバルスコープ内にない変数または関数は、関数スコープ内またはローカルスコープ内にあります。以下のリストを考えてみましょう。
function foo() {
var doo = "doo";
console.log(doo);
}
foo();
関数fooの関数スコープ内にある変数dooを作成しました。変数 doo の有効期間は関数 foo に対してローカルであり、その関数の外部ではアクセスできません。これは、JavaScript ではローカル スコープと呼ばれます。 下の図に示すコードリストを考えてみましょう。

ここでは、関数fooにグローバルスコープの変数と同じ名前の変数を作成したので、2つの変数があります。これらの変数は、それぞれの寿命を持つ2つの異なる変数であることに留意する必要があります。関数の外部では、値aの変数dooにアクセスできますが、関数fooの内部では、vale dooの変数dooが存在します。参考までに、上記のコードを以下に示します。
var doo = "a";
function foo() {
var doo = "doo";
console.log(doo); //print doo
}
foo();
console.log(doo); //print a
Let us tweak the above code snippet a bit as shown in listing below:
var doo = "a";
function foo() {
doo = "doo";
console.log(doo); //print doo
}
foo();
console.log(doo); //print doo
これで、変数 doo のスコープが 2 つありません。関数 foo 内では、グローバル スコープで作成された変数 doo が変更されています。 foo 内で変数 doo を再作成するのではなく、グローバル スコープから既存の変数 doo を変更します。

ローカルスコープまたは関数スコープで変数を作成するときは、キーワードvarを使用して変数を作成する必要があります。 それ以外の場合、変数はグローバルスコープに作成されるか、変数がグローバルスコープにすでに存在する場合は変更されます。
JavaScriptでは、関数の中に関数を持つことができます。任意のレベルのネストされた関数が存在する可能性があり、つまり、任意の数の関数を互いにネストできます。 以下のリストを考えてみましょう。
function foo() {
var f = "foo";
function doo() {
console.log(f);
}
doo();
}
foo(); //print foo
基本的に、上記のスニペットには、関数foo内に作成された関数dooがあり、独自の変数はありません。関数fooはローカル変数を作成し、関数doo内でアクセスできます。関数 doo は関数 foo の内部関数であり、関数 foo の変数にアクセスできます。また、関数 doo は関数 foo の本体内で呼び出すことができます。関数 doo は親関数で宣言された変数にアクセスできますが、これは JavaScript の字句スコープによるものです。
ここでは、スコープには 2 つのレベルがあります。
- Parent function foo scope
- Child function doo scope

関数 doo 内で作成された変数は、JavaScript の字句スコープにより、関数 foo のスコープ内で作成されたすべてのものにアクセスできます。ただし、関数 foo は関数 doo の変数にアクセスできません。

Closures in JavaScript
例を挙げて、JavaScript のクロージャを理解しましょう。以下に示すようにリストを検討してください。関数fooの本体内で関数dooを呼び出す代わりに、関数fooから関数dooを返します。
function foo() {
var f = "foo";
function doo() {
console.log(f);
}
return doo;
}
var afunct = foo();
afunct();
上記のリストでは、次のようになります。
- 関数 foo は別の関数 doo を返しています
- 関数 doo には独自の変数がありません
- 字句スコープにより、関数 doo は親関数 foo の変数にアクセスできます
- 関数 foo が呼び出され、変数 afunct に割り当てられます
- 次に、afunctが関数として呼び出され、文字列 "foo"を出力します。

驚くべきことに、上記のコードスニペットの出力は文字列 "foo" です。ここで、変数fは関数fooの外でどのようにアクセスされますか?通常、関数内のローカル変数は、その関数の実行中のみ存在します。したがって、理想的には、foo の実行後、変数 f にアクセスできなくなります。しかし、JavaScriptでは、afunctがJavaScriptクロージャになっているため、アクセスできます。クロージャ afunct には、関数 doo と、関数 doo のすべてのローカル スコープ変数に関する情報があります。

クロージャの場合、内部関数は外部関数スコープの参照を保持します。したがって、クロージャーでは:
- Inner 関数は、外部関数スコープの参照を保持します。この場合、関数 doo は関数 foo スコープの参照を保持します。
- 関数 doo は、外部関数 foo の実行が終了した場合でも、いつでも関数の food スコープ参照から変数にアクセスできます。
- JavaScript は、内部関数が存在して参照するまで、外部関数 (この場合は foo) のスコープ参照とその変数をメモリに保持します。この場合、関数 foo のスコープと変数は、関数 doo が存在するまで、JavaScript によってメモリに保持されます。
クロージャーをよりよく理解するために、もう 1 つの例について説明しましょう。
function add(num1) {
function addintern(num2) {
return num1 + num2;
}
return addintern;
}
var sum9 = add(7)(2);
console.log(sum9);
var sum99 = add(77)(22);
console.log(sum99);
ここには、sum9とsum99の2つのクロージャーがあります。
クロージャsum9が作成されたとき、関数addinternのローカルスコープではnum1の値は7であり、JavaScriptはsum9クロージャを作成するときにその値を記憶します。
クロージャsum99の場合も同様で、関数のローカルスコープでは、num1のaddintern値は7であり、JavaScriptはクロージャsum99を作成するときにその値を記憶します。 予想通り、出力は 9 と 99 になります。
クロージャは、プライベート変数と1つのメソッドを持つオブジェクトと考えることができます。クロージャを使用すると、そのデータを処理する関数にデータを添付できます。 したがって、クロージャは次の特性で定義できます。
- It is an object
- 関数が含まれています
- クロージャが作成されたときの関数のローカルスコープの変数を含む、関数に関連付けられたデータを記憶します
- クロージャを作成するには、関数は別の関数参照を返す必要があります
最後に、クロージャを定義できます。
「JavaScriptクロージャは、関数と関数が作成された環境を含む特別な種類のオブジェクトです。ここで、環境は、クロージャ作成時の関数とそのすべての変数のローカルスコープを表します。」
結論
この投稿では、JavaScript のクロージャについて学びました。お役に立てば幸いです。読んでくれてありがとう!
[2019年3月22日更新]