ホームページ  >  記事  >  ウェブフロントエンド  >  Javascript の入れ子関数と Closures_JavaScript スキルに関する簡単な説明

Javascript の入れ子関数と Closures_JavaScript スキルに関する簡単な説明

WBOY
WBOYオリジナル
2016-05-16 18:16:43930ブラウズ
【入れ子関数】
JavaScript では関数の埋め込みが可能であり、関数をデータとして使用することができ、関数の字句スコープの下で、従来のオブジェクト指向言語と驚くべき違いを生み出すことができます。
まず第一に、JavaScript 関数は動的ではなく字句的にスコープ設定されているため、関数は実行されるスコープではなく、定義されているスコープ内で実行されるため、ネストされた場合は簡単に理解できます。関数とその周囲の関数は同じ字句範囲内で定義されます。たとえば、次のような非常に当たり障りのないコード:
コード をコピーします。 コードは次のとおりです:

var x = 'グローバル';
関数 f () {
var x = 'ローカル';
関数 g() {
アラート(x); ();
}
f(); // 'local'


f() が呼び出されるとき、スコープ チェーンは呼び出しを含む 2 つの部分で構成されると理解できます。 f オブジェクトの後にグローバル オブジェクトが続きます。このとき、x の値を検索する場合は、まず f の呼び出しオブジェクトから検索され、見つからない場合は後続のグローバル オブジェクト内で x が検索されます。同様に、g は f の入れ子関数であるため、g が呼び出されるとき、スコープ チェーンは、g の呼び出しオブジェクト、f の呼び出しオブジェクト、およびグローバル オブジェクトの 3 つの部分で構成される必要があります。関数 g は x の値を出力するため、最初に g の呼び出しオブジェクトで x の値を検索します。次に、周辺の f 呼び出しオブジェクトで x の定義を検索します。 find x='local' の場合、グローバル オブジェクトの検索を続行する代わりに x を出力します。 x の値が f で定義されていない場合、スコープ チェーンの背後にあるグローバル オブジェクトの検索が続行され、結果はグローバルになります。グローバルオブジェクトで定義されていない場合は、当然未定義になります。
スコープ チェーンについては予備的に理解しましたが、同時に、クロージャには 2 つの一般的な用途があることもわかりました。1 つはローカル変数にアクセスするために使用できるということです。フィールド内の変数値はメモリに保存され、関数呼び出しが完了した後も破棄されません。
次に、クロージャが外部変数の値をメモリに保存できる理由を理解するのに役立つ、ありふれた例を見てみましょう。


function makeFunc (x) {
return function ( ) {return x }
}
var a = [makeFunc(0), makeFunc(1), makeFunc(2)];
alert(a[0]()); >alert( a[1]());
alert(a[2]());


実行結果は 0,1,2 です。字句スコープの厳密な通常の動作。各 makeFunc 呼び出しが完了すると、その呼び出し元オブジェクトはスコープ チェーンから削除され、そのオブジェクトへの参照はなくなり、最終的にはガベージ コレクションを通じて完了します。もっと詳しく言うと、このように理解できます。
makeFunc が呼び出されるたびに、呼び出し元のオブジェクトが作成され、スコープ チェーンに配置されます。関数 makeFunc の場合、呼び出し元オブジェクトには属性が含まれます。関数が実行されると、呼び出し元オブジェクトが作成され、スコープ チェーンに配置されます (注: 関数には x の定義がありません)。無名関数の呼び出しオブジェクトなので、その周囲の関数を参照します。 makeFunc の呼び出しオブジェクト (アクセスします。呼び出し元のオブジェクトには x が含まれているため、x が破棄されると x も破棄されます。保存されません。
上記は関数の詳細な実行プロセスです。よく理解して、以下の変更されたコードを見てください:



コードをコピー コードは次のとおりです: var x = 0;
function makeFunc () {
return function () {return x }
}
var a = [ makeFunc()、makeFunc()、makeFunc()];
alert(a[0]());
alert(a[2]()); );


x はグローバル変数であり、実行結果は 0、1、2 ですが、この結果は上記とは多少異なります。次に、スコープチェーンの観点からこの結果の理由を理解します。
同様に、makeFunc が呼び出されるたびに、内部の入れ子関数への参照が返されるため、呼び出し元オブジェクトがスコープ チェーンに作成され、内部の入れ子関数が実行を開始し、入れ子関数の呼び出しオブジェクトを作成します。スコープチェーン。次に、x の値を返します。ここでは、入れ子になった関数の呼び出しオブジェクトには x がなく、その周囲の makeFunc の呼び出しオブジェクトには x がありません。検索できるのはグローバル オブジェクトまでだけです。グローバル オブジェクト内で x の定義が見つかったので、通常どおり実行され、x の値が返され、x が 1 ずつ増分され、ネストされた関数が完了し、呼び出し元のオブジェクトが削除され、makeFunc が完了します。呼び出し元のオブジェクトも削除されますが、呼び出し元のオブジェクトには x がないため、呼び出し元のオブジェクトの破棄は x にまったく影響しません。その結果、グローバル変数 x の値の変更が保存されます。
外部呼び出しオブジェクトへの上記のアクセスは理解を助けるためのものであり、厳密なものではないことに注意してください。ただし、JavaScript が定義するプロパティはスコープ チェーンの一部です。呼び出しオブジェクトはまだ「生きています」。さらに、周辺関数にグローバル オブジェクトへの参照を持つ 2 つ以上の入れ子関数が含まれている場合、これらの入れ子関数はすべて同じグローバル呼び出しオブジェクトを共有し、そのうちの 1 つによるグローバル オブジェクトへの変更は他の関数にも表示されます。の。
JavaScript では、関数は、実行されるコードと、これらのコードが実行されるスコープで構成される複合体です。大まかに言えば、このコードとスコープの複合体をクロージャーと呼ぶことができます。
【終わり】
変数の値を記憶するために呼び出す必要がある関数を記述する必要がある場合があります。したがって、スコープを理解していれば、関数の呼び出しオブジェクトは呼び出し後に維持できないため、ローカル変数を実現するのが難しいことがわかります。上の例のように、グローバル変数でこれを行うことができますが、これは簡単にグローバル変数汚染を引き起こす可能性があります。呼び出し元のオブジェクトは維持できないので、呼び出し元のオブジェクトに値を保存すればよいのではないでしょうか? !したがって、これを行う 1 つの方法は、関数オブジェクト自体のプロパティとともに保存することです。
コードをコピー コードは次のとおりです。

uniqueID = function () {
if (! argument.callee.id) argument.callee.id = 0;
return argument.callee.id ;
alert(uniqueID()); //0
alert(uniqueID) ()) ; //1

上記のように、関数自体はオブジェクトなので、独自の属性の 1 つを付けて保存することは可能ですが、これには問題があります。つまり、誰でもいつでも保存できます。 unqueID.id を通じて最初に保存した値に強制的にアクセスし、変更を加えることができます。これは私たちが見たくないものです。
したがって、通常、これを達成するためにクロージャを使用します。次のように:

コードをコピー コードは次のとおりです:
_uniqueID = (function() {
var id = 0;
return function () {return id }
})();
alert(_uniqueID()) //0
alert(_uniqueID()); ; //1

同様に、use ドメインを使用して結果を説明します。 _uniqueID 自体は匿名関数であり、その中に匿名の入れ子関数があることに注意してください。直接呼び出すのは _uniqueID() です。つまり、直接呼び出すのは実際には _uniqueID 内の入れ子関数であり、それが呼び出し元のオブジェクトです。それ自体は ID を定義しないため、周囲の呼び出しオブジェクトの ID を参照し、それを返します。ID は 1 ずつ増加します。実行が完了すると、内側のネストされた関数呼び出しオブジェクトはスコープ チェーンの外に移動します。ペリフェラルIDは破壊されていなかったので、このように保存されました。
混乱している人もいるかもしれませんが、関数の実行後に呼び出し元のオブジェクトがスコープ チェーンを削除したということではないでしょうか?呼び出し元のオブジェクトにはあってはならないのです。
はい、呼び出し元のオブジェクトは現在の関数が実行された後に参照を終了しますが、上記の _uniqueID() 呼び出しは直接呼び出された周辺関数ではなく、ネストされた関数が呼び出されたものであることを誤解しないでください。関数のセットには、周囲の関数のスコープ チェーンが含まれます。したがって、呼び出し元のオブジェクトがスコープ チェーンを削除すると、このスコープ チェーン内の他のオブジェクトのプロパティにアクセスして変更できます。
クロージング自体は理解するのが難しいですが、困っている人の助けになれば幸いです。また、資格が限られているため、私の理解が間違っている可能性があります。見つけた場合は修正してください。
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。