補充:
閉鎖は、JavaScript言語とその機能の難しさです。多くの高度なアプリケーションは、閉鎖に依存して実装しています。
閉鎖機能
閉鎖には3つの特性があります。
1。ネストされた関数関数
2。関数は内部の外部パラメーターと変数を参照できます
3.パラメーターと変数は、ごみ収集メカニズムによって収集されません
閉鎖とその利点と短所の定義
閉鎖は、別の関数の範囲内の変数にアクセスできる関数を指します。閉鎖を作成する最も一般的な方法は、ある関数内で別の関数を作成し、別の関数を介してこの関数のローカル変数にアクセスすることです。
閉鎖の欠点は、それらが常駐メモリであり、これがメモリの使用量を増やすことであり、不適切な使用によりメモリの漏れが容易につながる可能性があります。
閉鎖は、JavaScript言語の主要な機能です。閉鎖の主な適用は、主にプライベートメソッドと変数の設計のためです。
一般的な関数が実行された後、ローカルアクティブオブジェクトが破壊され、グローバルスコープのみがメモリに保存されます。しかし、閉鎖状況は異なります!
トピックについて話す
1。閉鎖の定義?
閉鎖に関するいくつかの定義を見てみましょう。
1。閉鎖とは、別の関数範囲内の変数にアクセスする許可を持つ関数を指します。
2。関数オブジェクトはスコープチェーンを介して関連付けられ、関数ボディ内の変数を関数の範囲に保存できます。この特性は「閉鎖」と呼ばれます。
3。内部関数は、それらを定義する外部関数のパラメーターと変数にアクセスできます(これと引数を除く)。
JS閉鎖の概念を体系的に学習したい場合は、wulin.comのJS電子書籍コラムを参照して学習できます。
定義を要約しましょう
1.外部関数範囲内の変数の関数にアクセスできます
2。内部関数によってアクセスされる外部関数の変数は、リサイクルされることなく外部関数の範囲内で保存できます。これがコアです。後で、閉鎖に遭遇したら、それを考える必要があります。閉鎖によって参照される変数に焦点を当てる必要があります。
簡単な閉鎖を作成します
var saysname = function(){var name = 'jozo'; return function(){alert(name);}}; var say = sayname();言う();次の2つの文を解釈しましょう。
•var says = sayname():変数Sakeに保存されている匿名の内部関数を返し、外部関数の変数名を参照します。 Garbage Collectionメカニズムにより、SayName関数が実行された後、変数名は破壊されません。
•say():返された内部関数を実行し、変数名と出力「jozo」にアクセスします。
2。閉鎖中のスコープチェーン
スコープチェーンの理解は、閉鎖を理解するのにも役立ちます。
スコープ内の変数の検索方法は非常によく知られている必要がありますが、実際、これはスコープチェーンに沿って上方に検索しているものです。
関数が呼び出された場合:
1.最初に実行コンテキストと対応するスコープチェーンを作成します。
2。関数のアクティブオブジェクト(アクティベーションオブジェクト)に引数とその他の指定されたパラメーター値を追加します
スコープチェーン:現在の関数のアクティブオブジェクトは最優先事項であり、その後に外部関数のアクティブオブジェクトが続き、外部関数の外部関数のアクティブオブジェクトは、スコープチェーンの最後までグローバルスコープまで順番に減少します。優先度は、可変検索の順序です。
まず、通常のスコープチェーンを見てみましょう。
function saysname(name){return name;} var say = sayname( 'jozo');このコードには2つのスコープが含まれています。グローバル範囲。 B.SayName関数スコープ、つまり、2つの変数オブジェクトのみがあります。対応する実行環境に実行されると、変数オブジェクトはアクティブオブジェクトになり、実行環境のスコープチェーンのフロントエンドにプッシュされます。つまり、最優先事項になります。写真に話しかける:
この写真は、JS Advanced Programming Booksでも入手でき、すべてを再描画しました。
sayname()関数を作成すると、変数オブジェクトを事前に含むスコープチェーン、つまり図の1 x 1個のスコープチェーンが作成され、内部[[scope]]属性に保存されます。 sayname()関数が呼び出されると、実行環境が作成され、その後、関数の[[scope]]属性のオブジェクトをコピーすることにより、スコープチェーンが構築されます。その後、別のアクティブオブジェクト(図に0個のインデックスが付けられています)が作成され、実行環境のスコープチェーンのフロントエンドに押し込まれます。
一般的に、関数が実行されると、ローカルアクティブオブジェクトが破壊され、グローバルスコープのみがメモリに保存されます。ただし、閉鎖の状況は異なります。
閉鎖の範囲チェーンを見てみましょう。
function saysname(name){return function(){return name;}} var say = sayname( 'jozo');この閉鎖インスタンスには、前の例よりも匿名関数の範囲が1つあります。
匿名関数がsayname()関数から返されると、そのスコープチェーンは、sayname()関数を含むアクティブオブジェクトとグローバル変数オブジェクトに初期化されます。このようにして、匿名関数は、sayname()で定義されているすべての変数とパラメーターにアクセスできます。さらに重要なことに、SayName()関数の実行後、匿名関数のスコープチェーンがアクティブオブジェクトを指すため、そのアクティブオブジェクトは破壊されません。言い換えれば、SayName()関数の実行後、実行環境のスコープチェーンは破壊されますが、そのアクティブなオブジェクトは匿名関数が破壊されることを知ってメモリに残されます。これは、後で説明するメモリリークの問題でもあります。
私はスコープチェーンの問題についてそれほど書いていません、そして、本に物事を書くことも非常に疲れますo(□)o
3。閉鎖の例
例1:蓄積の実装
// 1var a = 0; var add = function(){a ++; console.log(a)} add(); add(); // methure add =(var a = 0; return function(){a ++; console.log(a);}})(); console.log(a); // undefinedadd(); add();それに比べて、方法2はよりエレガントであり、グローバル変数を減らし、変数を民営化します。
例2:各LIにクリックイベントを追加します
var oli = document.getElementsBytagname( 'li'); var i; // 5 //匿名関数を実行(function(){alert(i); // 5}());上記は古典的な例です。実行結果は5がポップアップすることを知っています。また、閉鎖を使用してこの問題を解決できることも知っていますが、最初は5がポップアップする理由と、クロージャーがこの問題を解決できる理由をまだ理解できません。後で、私はそれを整理して明確にしました:
a。最初に閉鎖が使用される前に状況を分析しましょう。FORループでは、匿名関数を各LIクリックイベントに結合し、変数Iの値は匿名関数で戻ります。ループが終了すると、変数Iの値は5になります。この時点で、各LI、つまり、対応する匿名関数をクリックします(上記のコードを参照)。これは既に5であるため、各クリックが5であるため、ここで返される各匿名関数は同じ変数Iを指すためです。ループが実行されたときにiの電流I値を保存する場合、匿名関数がこの変数を適用し、最後にこの匿名関数を返して、目的を達成できるようにします。これは閉鎖を使用して達成されます!
b。閉鎖を使用するときの状況を分析しましょう。
var oli = document.getElementsByTagname( 'li'); var i; // 5
forループが実行されると、クリックイベントにバインドされた匿名関数が渡され、すぐに実行されて内部匿名関数を返します。パラメーターは値で渡されるため、正式なパラメーター数はiの現在の値を保存し、ローカル変数aに値を割り当てます。次に、内部匿名関数は、Aの参照を保持します。つまり、iの現在の値を保持します。したがって、ループが実行されたら、各LIをクリックすると、返された匿名関数が保存されたaの値をポップアップします。
4。閉鎖の適用
閉鎖の目的を見てみましょう。実際、閉鎖を使用することで、多くのことができます。たとえば、オブジェクト指向のコードスタイルをシミュレートします。コードをよりエレガントかつ簡潔に表現します。いくつかの面でコード実行効率を改善します。
1。匿名の自己実行機能
実際の状況では、一部の機能を1回だけ実行する必要があり、UIの初期化など、内部変数を維持する必要がない状況に遭遇することがよくあります。
//すべてのLiフォントを赤(function(){var els = document.getelementsbytagname( 'li'); for(var i = 0、lng = els.length; i <lng; i ++){els [i] .style.color = 'red';}})()();匿名関数を作成し、すぐに実行します。外部は内部の変数を参照できないため、ELS、I、LNGなどのローカル変数は実行後すぐにリリースされ、メモリが保存されます!
重要なのは、このメカニズムがグローバルオブジェクトを汚染しないことです。
2。カプセル化/モジュラーコードを実装します
var person = function(){//変数のスコープは関数内にあり、var name = "default"に外部にアクセスできません。 return {getName:function(){return name; }、setName:function(newName){name = newName; }}}(); console.log(person.name); //直接アクセス、結果は未定義のconsole.log(person.getname()); // default person.setname( "jozo"); console.log(person.getName()); // Jozo3。オブジェクト指向を実装します
このようにして、異なるオブジェクト(クラスのインスタンス)には独立したメンバーと状態があり、互いに干渉しません。 JavaScriptのクラスのようなメカニズムはありませんが、閉鎖を使用することにより、そのようなメカニズムをシミュレートできます。上記の例について話しましょう:
function person(){var name = "default"; return {getName:function(){return name; }、setName:function(newName){name = newName; }}}; var person1 = person(); print(person1.getname()); john.setname( "person1"); print(person1.getname()); // person1 var person2 = person(); print(person2.getName()); Jack.setName( "erson2"); print(erson2.getname()); // person2人の2つのインスタンスperson1とperson2は互いに干渉しません!これらの2つのインスタンスは、名前メンバーに独立してアクセスできるためです。
5。メモリリークとソリューション
ゴミリサイクルメカニズム
メモリ管理といえば、JSのごみ収集メカニズムとは自然に分離できません。ガベージコレクションを実現するための2つの戦略があります。マーククリアランスと参照カウント。
マークの削除:ガベージコレクターが実行されると、メモリに保存されているすべての変数がマークされます。次に、環境内の変数のタグと、環境の変数によって参照される変数のタグを削除します。その後、変数が再びマークされている場合、変数を削除する準備ができていることを意味します。 2008年まで、IE、Firefox、Opera、Chrome、およびSafariのJavaScriptはすべてこの方法を使用していました。
参照カウント:各値が参照される回数を追跡します。変数が宣言され、参照タイプの値が変数に割り当てられる場合、この値が参照される回数は1です。この値が別の変数に割り当てられている場合、参照は1。値の参照から逸脱すると、値の参照数が1、garbageの場合は1つ、
この方法の大きな問題は、循環参照です。つまり、オブジェクトAにはBへのポインターが含まれ、オブジェクトBにはAへの参照も含まれています。閉鎖がメモリの漏れを引き起こした理由の1つは、このアルゴリズムの欠陥でした。
IEの一部のオブジェクトはネイティブJavaScriptオブジェクトではないことを知っています。たとえば、BOMおよびDOMのオブジェクトはCOMオブジェクトの形で実装され、COMオブジェクトのガベージコレクションメカニズムは参照カウントを採用します。したがって、IEのJavaScriptエンジンはタグクリアリング戦略を採用していますが、COMオブジェクトへのアクセスは参照カウントに基づいているため、COMオブジェクトがIEで設計されている限り、循環参照の問題があります。
栗を取る:
window.onload = function(){var el = document.getElementById( "id"); el.onclick = function(){alert(el.id);}}}このコードがメモリリークを引き起こすのはなぜですか?
el.onclick = function(){alert(el.id);};このコードを実行すると、匿名関数オブジェクトはELのonClick属性に割り当てられます。次に、匿名関数は内部のELオブジェクトを指し、円形の参照があるため、リサイクルできません。
解決:
window.onload = function(){var el = document.getElementbyId( "id"); var id = el.id; // unreference el.onclick = function(){alert(id); } el = null; //閉鎖によって参照される外部関数のアクティブオブジェクトをクリア}上記は、編集者が紹介したJSクロージャースコープチェーンガベージコレクションメモリリークに関する関連する知識の要約です。私はそれが誰にでも役立つことを願っています!