ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript クロージャとは何かについての深い理解_基礎知識

JavaScript クロージャとは何かについての深い理解_基礎知識

WBOY
WBOYオリジナル
2016-05-16 17:37:26875ブラウズ

1. 簡単な例

典型的な間違いから始めましょう。ページには onclick メソッドをバインドしたいので、次のコードがあります。

Copyコード コードは次のとおりです。


/span> 2 3
;


;/span> ;2 3
>

コードをコピーします
コードは次のとおりです:$(document).ready(function() { var spans = $("#divTest span");
for (var i = 0; i spans [i] .onclick = function(){
alert(i);
;




コードをコピー

コードは次のとおりです。

var spans2 = $("#divTest2 span ");

$ (docume).READY (function () { for (var I = 0; I & lt; spans2.length; i) { (function (num) {スパン2 [i] .onclick = function() { });

2. 内部関数


基本的な知識から始めて、まず内部関数を理解しましょう。内部関数は、別の関数内で定義された関数です。例:




コードをコピー

コードは次のとおりです。

function innerFn () { functioninnerFn () {}

}


innerFn は、outerFn スコープにラップされた内部関数です。これは、outerFn 内での innerFn の呼び出しは有効ですが、outerFn の外での innerFn の呼び出しは無効であることを意味します。次のコードは JavaScript エラーを引き起こします: コードをコピー

コードは次のとおりです。


function innerFn() {
document .write( "外部関数
");
function innerFn() {

document.write("内部関数
");

}

} innerFn( );
ただし、innerFn が externalFn 内で呼び出された場合は、正常に実行できます。

コードをコピー コードは次のとおりです。

function innerFn() {
document .write( "外部関数
");
function innerFn() {
document.write("内部関数
");
}
innerFn() ;
}
innerFn();

2.1 大脱走

JavaScript を使用すると、開発者はあらゆる種類のデータと同様に関数を渡すことができます。つまり、JavaScript の内部関数は、それを定義する外部関数をエスケープできます。

エスケープする方法は多数あります。たとえば、次のように内部関数をグローバル変数に割り当てることができます。

コードをコピーコードは次のとおりです。

var globalVar;
function innerFn() {
document.write("Outer function
"); ){
document .write( "Inner Function< br/>");

その後、globalVar を呼び出すことは innerFn を呼び出すことと同じになります。この時点で、innerFn を externalFn の外部で直接呼び出すと、依然としてエラーが発生します。これは、inner 関数は参照をグローバル変数に保存することでエスケープされますが、この関数の名前は依然として externalFn のスコープ内にのみ存在するためです。

親関数の戻り値を通じて内部関数参照を取得することもできます



コードをコピー

コードは次のとおりです。

return innerFn;
}
var fnRef = innerFn();
fnRef();


グローバル変数は、outerFn 内では変更されませんが、innerFn への参照が、outerFn から返されます。この参照は、outerFn を呼び出すことで取得でき、変数に保存できます。


関数スコープを抜けた後でも内部関数を参照によって呼び出すことができるということは、内部関数を呼び出す可能性がある限り、JavaScript は参照先の関数を保持する必要があることを意味します。さらに、JavaScript ランタイムは、JavaScript ガベージ コレクターが対応するメモリ領域を解放する前に、最後の変数が破棄されるまで、この内部関数を参照するすべての変数を追跡する必要があります (赤い部分がクロージャを理解するための鍵です)。

長い間話してきましたが、ついにクロージャについて話します。クロージャとは、別の関数のスコープ内の変数にアクセスする権限を持つ関数を指します。クロージャを作成する一般的な方法は、別の関数を作成することです。これは上で述べた内部関数なので、私が今言ったことはナンセンスではなく、クロージャにも関係します^_^

1.2 変数のスコープ

内部関数は、内部関数のスコープに制限される独自の変数を持つこともできます。



コードをコピー


コードは次のとおりです:


function innerFn() {
document.write("外部関数
");
document.write(" 内部関数"); }
var fnRef = innerFn();
var fnRef2 = innerFn();
fnRef();
fnRef2();


この内部関数が参照または他の関数によって呼び出されるたびつまり、新しい innerVar 変数が作成され、1 ずつ増分され、最後に
が表示されます。




コードをコピー

コードは次のとおりです。

外部関数内部関数 innerVar = 1内部関数 innerVar = 1外部関数内部関数 innerVar = 1内部関数 innerVar = 1
内部関数は、他の関数と同様にグローバル変数を参照することもできます。





コードをコピー

コードは次のとおりです:

var globalVar = 0; function innerFn() { . 🎜> document. globalVar "
"); ‐‐‐‐‐‐‐‐‐‐ f(); var fnRef2 = innerFn(); var fnRef2 = innerFn(); fnRef2(); 変数​​の値:




コードをコピー


コードは次のとおりです。


外部関数
内部関数 globalVar = 1
内部関数 globalVar = 2
外部関数
内部関数 globalVar = 3
内部関数 globalVar = 4

しかし、この変数が次のローカル変数である場合はどうなるでしょうか。親関数?内部関数は親関数のスコープを参照するため (興味があれば、スコープ チェーンとアクティブ オブジェクトについて学ぶことができます)、内部関数はこれらの変数も参照できます

コードをコピー コードは次のとおりです:

関数 innerFn() {
var externalVar = 0;
document.write("アウター関数
");
innerFn() 関数 {
外側Var ;
document.write("内部関数"); 🎜> }
var fnRef = innerFn();
var fnRef2 = innerFn();
fnRef();
fnRef2();


今回の結果は非常に興味深いもので、おそらく私たちの予想を超えています




コードをコピー

コードは次のとおりです。外部関数内部関数 externalVar = 1内部関数outerVar = 2外部関数
内部関数outerVar = 1
内部関数outerVar = 2


ここで確認できるのは、前の 2 つの効果を組み合わせたものです。 innerFn への各参照呼び出しは、outerVar を個別にインクリメントします。つまり、outerFn への 2 番目の呼び出しは、outerVar の値を引き続き使用せず、2 番目の関数呼び出しのスコープ内に新しい externalVar インスタンスを作成してバインドします。2 つのカウンターはまったく関連しません。

内部関数が定義されているスコープ外で参照されると、内部関数のクロージャが作成されます。この場合、内部関数のローカル変数でもパラメータでもない変数を自由変数と呼び、外部関数の呼び出し環境をクローズドクロージャ環境と呼びます。基本的に、内部関数が外部関数にある変数を参照する場合、その変数の遅延が許可されます。したがって、外部関数の呼び出しが完了しても、これらの変数のメモリは解放されず (最後の値が保存され)、クロージャは依然としてそれらを使用する必要があります。


3. クロージャ間の相互作用

内部関数が複数ある場合、予期しないクロージャが発生する可能性があります。増加関数を定義します。この関数の増分は 2

です。

コードをコピー

コードは次のとおりです:

function innerFn() {
var innerVar = 0;
document.write("Outer function
");
function innerFn1() {
externalVar ;
document.write("内部関数 1t");
document.write("outerVar = " innerVar "
");
}

function innerFn2() {
externalVar = 2;
document.write("Inner function 2t");
document.write("outerVar = " "
") ;
}
return { "fn1": innerFn1, "fn2": innerFn2 };
}
var fnRef = innerFn();
fnRef.fn 1();
fnRef .fn2();
fnRef.fn1();
var fnRef2 = innerFn();
fnRef2.fn1();
fnRef2.fn2();
fnRef2。 fn1() ;


我们射は 2 つの内部関数の参照を返します、表示を介して返される参照を使用できます、結果:

复制代代码如下:

外部関数
内部関数 1 externalVar = 1
内部関数 2 outerVar = 3
内部関数 1 outerVar = 4
外部関数
内部関数 1 outerVar = 1
内部関数 2 outerVar = 3
内部関数 1 outerVar = 4

innerFn1 と innerFn2 は同じ局所的な値を参照するため、どちらも閉鎖的な環境を共有します。 innerFn1 が outerVar になると、innerFn2 は outerVar の新しい開始値を設定します。外部 Fn へのその後の調整は、これらの閉包の新しい例を構築すると同時に、新しい閉包環境も構築します。本質的には、新しいオブジェクトが構築されており、自由量はこのオブジェクトの例の量であり、閉包はこれですオブジェクトの例示的な方法であり、これらの値は、それらをカプセル化する作用領域の外部から直接参照することができないため、対面オブジェクトデータの固有性を確保するために私有のものである。

4.解不安

私たちが見返すことができる、シティ・アラート 4 の例は、毎回シティ・アラート 4 の最初のタイプの書き込み法であることは明らかです。 🎜>

代码如下:

for (var i = 0; i spans[i].onclick = function() { アラート(i); } }

iの値が4の場合、上記コードは判定条件が成立せずforループが実行されますが、各スパンのonclickメソッドは内部関数であるため、今回は、i は閉じられています。参照、メモリは破棄できません。i の値は 4 のままで、プログラムが変更するか、すべての onclick 関数が破棄されるまでリサイクルされません (関数を null にアクティブに割り当てるか、ページがアンロードされます)。 )。このようにして、span をクリックするたびに、onclick 関数は i の値を検索し (スコープ チェーンは参照メソッドです)、それが 4 に等しい場合は警告を発します。 2 番目の方法は、すぐに実行される関数を使用してクロージャーの層を作成する方法です。関数宣言は括弧と括弧を追加した後、式になります。このとき、i はパラメーターとして使用されます。渡されると、関数はすぐに実行され、num は毎回 i の値を保存します。

これを読んだ後は、誰もが私と同じようにクロージャについてある程度理解できるはずです。もちろん、完全に理解した場合は、関数の実行環境とスコープ チェーンを理解する必要があります。^_^

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。