一言で言えばJavascriptのクロージャする機能できるように変数にアクセスされる字句-親関数内で宣言を。
より詳細な説明を見てみましょう。クロージャを理解するには、JavaScriptが変数をどのようにスコープするかを理解することが重要です。
スコープ
JavaScriptでは、スコープは関数で定義されます。すべての関数が新しいスコープを定義します。
次の例を検討してください。
function f()
{//begin of scope f
var foo='hello'; //foo is declared in scope f
for(var i=0;i<2;i++){//i is declared in scope f
//the for loop is not a function, therefore we are still in scope f
var bar = 'Am I accessible?';//bar is declared in scope f
console.log(foo);
}
console.log(i);
console.log(bar);
}//end of scope f
f printsの呼び出し
hello
hello
2
Am I Accessible?
関数g
が別の関数内で定義されている場合を考えてみましょうf
。
function f()
{//begin of scope f
function g()
{//being of scope g
/*...*/
}//end of scope g
/*...*/
}//end of scope f
f
の字句親を呼び出しますg
。前に説明したように、2つのスコープがあります。スコープf
とスコープg
。
しかし、1つのスコープは他のスコープの「内」にあるので、子関数のスコープは親関数のスコープの一部ですか?親関数のスコープで宣言された変数で何が起こるか。子関数のスコープからそれらにアクセスできますか?それはまさにクロージャーが介入するところです。
閉鎖
JavaScriptでは、関数g
はスコープで宣言された変数にアクセスできるだけでなくg
、親関数のスコープで宣言された変数にもアクセスできますf
。
以下を検討してください。
function f()//lexical parent function
{//begin of scope f
var foo='hello'; //foo declared in scope f
function g()
{//being of scope g
var bar='bla'; //bar declared in scope g
console.log(foo);
}//end of scope g
g();
console.log(bar);
}//end of scope f
f printsの呼び出し
hello
undefined
行を見てみましょうconsole.log(foo);
。この時点でスコープにg
入り、スコープでfoo
宣言された変数にアクセスしようとしますf
。しかし前に述べたように、レキシカル親関数で宣言された変数にアクセスできます。g
はの字句親ですf
。したがってhello
、印刷されます。
次に、ラインを見てみましょうconsole.log(bar);
。この時点でスコープにf
入り、スコープでbar
宣言された変数にアクセスしようとしますg
。bar
は現在のスコープで宣言されておらず、関数g
はの親ではないf
ためbar
、未定義です
実際、字句「大親」関数のスコープで宣言された変数にアクセスすることもできます。したがって、関数h
内で定義された関数がある場合g
function f()
{//begin of scope f
function g()
{//being of scope g
function h()
{//being of scope h
/*...*/
}//end of scope h
/*...*/
}//end of scope g
/*...*/
}//end of scope f
その後、h
すべての変数にアクセスできるようになる機能の範囲内で宣言h
、g
とf
。これはクロージャで行われます。JavaScript クロージャでは、レキシカル親関数、レキシカルグランド親関数、レキシカルグランドグランド親関数などで宣言された変数にアクセスできます。これはスコープチェーンと見なすことができます。 scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ...
字句の親を持たない最後の親関数まで。
ウィンドウオブジェクト
実際には、チェーンは最後の親関数で停止しません。もう1つの特別なスコープがあります。グローバルスコープ。関数で宣言されていないすべての変数は、グローバルスコープで宣言されていると見なされます。グローバルスコープには2つの専門分野があります。
- グローバルスコープで宣言されたすべての変数はどこからでもアクセス可能
- グローバルスコープで宣言された変数は、
window
オブジェクトのプロパティに対応します。
したがってfoo
、グローバルスコープで変数を宣言する方法は2つあります。関数で宣言しないかfoo
、ウィンドウオブジェクトのプロパティを設定します。
どちらの試みもクロージャーを使用しています
より詳細な説明を読んだところで、両方のソリューションがクロージャを使用していることは明らかです。しかし、確かに、証明を作ってみましょう。
新しいプログラミング言語を作成しましょう。JavaScript-閉鎖なし。名前が示すように、JavaScript-No-ClosureはJavaScriptと同じですが、クロージャーをサポートしていません。
言い換えると;
var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello
では、JavaScript-No-Closureを使用した最初のソリューションで何が起こるか見てみましょう。
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}, 1000)
})();
}
したがって、これはundefined
JavaScript-No-Closureで10回印刷されます。
したがって、最初のソリューションはクロージャーを使用します。
2番目のソリューションを見てみましょう。
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}
})(i), 1000);
}
したがって、これはundefined
JavaScript-No-Closureで10回印刷されます。
どちらのソリューションもクロージャを使用しています。
編集:これらの3つのコードスニペットはグローバルスコープで定義されていないことが前提です。それ以外の場合は、変数foo
とするi
と結合することになるwindow
オブジェクトので、介してアクセス可能なwindow
JavaScriptとJavaScriptの-NO-クロージャーの両方でオブジェクト。