序文:それはまだ入門的な記事です。 JavaScriptには、オブジェクト、プロトタイプの継承、閉鎖など、いくつかの非常に重要な言語機能があります。その中でも、閉鎖は、従来の静的言語C/C ++を使用するプログラマー向けの新しい言語機能です。この記事では、JavaScriptクロージャーの言語機能を紹介する例から始まり、いくつかのECMAScript言語仕様を組み合わせて、読者が閉鎖をより深く理解できるようにします。
注:この記事は入門的な記事であり、サンプル資料はインターネットにまとめられています。あなたがマスターなら、記事に関する技術的な提案や意見を提出することを歓迎します。この記事では、JavaScriptについて説明しますが、言語を比較したくありません。 JavaScriptに自然に不快な場合は、迂回してください。
閉鎖とは何ですか
閉鎖とは何ですか?閉鎖は閉鎖です。これは、静的言語にはない新しい機能です。しかし、閉鎖は非常に複雑なものではなく、理解できないものではありません。要するに、閉鎖は次のとおりです。
閉鎖は関数のローカル変数のセットですが、これらのローカル変数は関数が戻ってきた後も存在し続けます。
閉鎖は関数の「スタック」であり、関数が戻ってきた後はリリースされません。また、これらの機能スタックはスタックに割り当てられていないが、ヒープに割り当てられていることも理解できます。
関数内で別の関数を定義すると、閉鎖が生成されます
上記の2番目の定義は、最初の定義の主題予測オブジェクトを抽出する最初の補足説明です。閉鎖は「ローカル変数」セットの関数です。このローカル変数に関数が戻った後にアクセスできるというだけです。 (これは公式の定義ではありませんが、この定義は閉鎖の理解をより助長する必要があります)
ローカル変数として、それらは関数のコードによってアクセスでき、この言語と静的言語の間に違いはありません。閉鎖の違いは、関数が実行された後、関数の外側のコードでローカル変数にアクセスできることです。これは、関数が閉鎖への「参照」を返すか、外部変数に「参照」を割り当てて、クロージャー内のローカル変数が外部コードによってアクセスされるようにする必要があることを意味します。もちろん、この参照を含むエンティティはオブジェクトである必要があります。これは、JavaScriptでは、基本タイプを除くすべての残りがオブジェクトであるためです。残念ながら、ECMAScriptは、閉鎖内のローカル変数にアクセスするための関連するメンバーと方法を提供していません。ただし、ECMAScriptでは、関数オブジェクトで定義されている内部関数は、外部関数に直接アクセスできるローカル変数です。このメカニズムを通じて、次の方法で閉鎖へのアクセスを完了できます。
コードコピーは次のとおりです。
関数グリーティング(名前){
var text = 'hello' + name; //ローカル変数
//閉鎖が生成されるたびに、内部関数オブジェクトが発信者に返されます
return function(){alert(text); }
}
var sayshello = greeting( "closure");
sayshello()//閉鎖を介してローカル変数テキストにアクセスする
上記のコードの実行結果は次のとおりです。こんにちは閉鎖は、グリーティング関数が実行された後、Sayhello()関数がその中に定義されたローカル変数テキストにアクセスできるためです。
わかりました、これは伝説的な閉鎖の効果です。閉鎖には、Singleton、Power Constructor、および閉鎖の使用と切り離せない他のJavaScriptモードなど、JavaScriptの多くのアプリケーションシナリオとモードがあります。
ECMAScript閉鎖モデル
ECMaScriptはどのように閉鎖を実装しますか?詳細な理解が必要な場合は、研究のためのECMAScript仕様を取得できます。ここでは簡単な説明のみを行いますが、コンテンツはインターネットからも届きます。
ECMAScriptスクリプトの関数が実行されると、各関数アソシエーションには、3つの部分を含む実行コンテキストシナリオ(実行コンテキスト)があります。
語彙ンビローンメント
可変環境
このバインディング
このバインディングの3番目のポイントは、閉鎖とは関係ありません。この記事では説明していません。関数実行プロセスを解析するために文法環境で使用される変数識別子。文法環境は、2つの重要なコンポーネント、環境記録(環境レコード)と外部参照(ポインター)を含むオブジェクトと考えることができます。環境レコードには、関数によって内部的に宣言されたローカル変数とパラメーター変数が含まれており、外部参照は外部関数オブジェクトのコンテキスト実行シナリオを指します。この参照値は、グローバルコンテキストシナリオではヌルです。このようなデータ構造は、一方向リンクリストを形成し、各参照は外部コンテキストシナリオを指します。
たとえば、上記の例の閉鎖モデルはこのようなものでなければなりません。 Sayhello関数は最低レベルで、上位レベルは関数の挨拶であり、最も外側のレベルはグローバルシーンです。下の図に示すように、したがって、Sayshelloが呼ばれると、Sayshelloはコンテキストシーンを介してローカル変数テキストの値を見つけます。したがって、「Hello Closure」変数環境(変動環境)と文法環境は基本的に同じです。具体的な違いについては、ECMAScript仕様文書を参照してください。
閉鎖のサンプル列
前の記事では、JavaScriptの閉鎖とは何か、JavaScriptで閉鎖がどのように実装されるかを大まかに理解しています。以下に、いくつかの例をターゲットにすることで、閉鎖をより深く理解するのに役立ちます。以下には5つの例があり、例はダミー(ミラー)のJavaScript閉鎖によるものです。例1:閉鎖のローカル変数はコピーではなく参照です
コードコピーは次のとおりです。
関数Says667(){
//閉鎖内になってしまうローカル変数
var num = 666;
var saysalert = function(){alert(num); }
num ++;
return saySalert;
}
var saysalert = say667();
SayAlert()
したがって、実行結果は666ではなく667をポップアップする必要があります。
例2:複数の関数は、同じ関数内で定義されているため、同じ閉鎖にバインドします。
コードコピーは次のとおりです。
関数setupsomeglobals(){
//閉鎖内になってしまうローカル変数
var num = 666;
//関数への参照をグローバル変数として保存します
galertnumber = function(){alert(num); }
gincreaseNumber = function(){num ++; }
gsetNumber = function(x){num = x; }
}
setupsomeglobals(); // 3つのグローバル変数に値を割り当てます
GalertNumber(); // 666
GincreasEnumber();
GalertNumber(); // 667
gsetNumber(12); //
GalertNumber(); // 12
例3:ループで機能を割り当てるとき、これらの機能は同じ閉鎖にバインドされます
コードコピーは次のとおりです。
function buildlist(list){
var result = [];
for(var i = 0; i <list.length; i ++){
var item = 'item' + list [i];
result.push(function(){alert(item + '' + list [i])});
}
返品結果;
}
function testlist(){
var fnlist = buildlist([1,2,3]);
//混乱を防ぐためにJのみを使用する - 私は使用できます
for(var j = 0; j <fnlist.length; j ++){
fnlist [j]();
}
}
テストリストの実行結果は、これらの3つの関数が同じ閉鎖に結合し、アイテムの値が最後に計算された結果であるため、item3未定義のウィンドウが3回ポップアップすることですが、ループからジャンプすると、I値は4であるため、リスト[4]の結果は未定義です。
例4:外部関数のすべてのローカル変数は、この変数が内部関数定義の後に宣言されたとしても、閉鎖中にあります。
コードコピーは次のとおりです。
関数saysalice(){
var saysalert = function(){alert(alice); }
//閉鎖内になってしまうローカル変数
var Alice = 'Hello Alice';
return saySalert;
}
var helloalice = sayalice();
helloAlice();
実行結果は、「Hello Alice」ポップアップのあるウィンドウです。関数が言われた後にローカル変数が宣言したとしても、ローカル変数にアクセスできます。
例5:関数が呼び出されるたびに新しい閉鎖を作成します
コードコピーは次のとおりです。
function newclosure(somenum、someref){
//閉鎖内に終わるローカル変数
var num = somenum;
var anarray = [1,2,3];
var ref = someref;
return function(x){
num += x;
anarray.push(num);
alert( 'num:' + num +
'/nanArray' + anarray.toString() +
'/nref.somevar' + ref.somevar);
}
}
closure1 = newClosure(40、{somevar: 'closure 1'});
closure2 = newClosure(1000、{somevar: 'closure 2'});
closure1(5); // num:45 anarray [1,2,3,45] ref: 'somevar closure1'
closure2(-10); // num:990 anarray [1,2,3,990] ref: 'Somevar closure2' '
閉鎖の適用
シングルトンシングルピース:
コードコピーは次のとおりです。
var singleton = function(){
var privatevariable;
関数privateFunction(x){
... privatevariable ...
}
戻る {
firstmethod:function(a、b){
... privatevariable ...
}、
SecondMethod:function(c){
... privateFunction()...
}
};
}();
この単一の作品は、閉鎖によって達成されます。プライベートメンバーと方法のカプセル化は、閉鎖によって完了します。匿名のメイン関数はオブジェクトを返します。オブジェクトには2つのメソッドが含まれています。方法1はプライベート変数を使用でき、方法2は内部プライベート機能にアクセスできます。注意すべきことは、匿名のメイン関数が終了する「()」です。この '()'がなければ、単一のピースを作成することはできません。匿名関数は一意のオブジェクトのみを返すことができ、他の場所で呼び出すことはできないからです。これは、閉鎖を使用して単一のピースを生成する方法です。