JS閉包の深さ理解

7535 ワード

クローズド(closure)はJavascript言語の一つの難点であり、その特色でもあり、多くの高級アプリケーションはクローズドによって実現されます.
クローズドを理解するには、まず変数のスコープの概念を理解します.1.変数のスコープjsには2つの変数があります.大域変数、局所変数jsには、関数の内部で大域変数を読み取ることができる特殊な点があります.
var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999
しかし、関数の外部ではローカル変数を直接読み込めません.
function f1(){
    var n=999;
  }

  alert(n); // error
関数内部で局所変数を宣言する場合はvarコマンドを使用します.そうでないと実際には大域変数です.
2.関数の外部は、関数内部の変数をどのように読み取りますか?
それは関数の内部で、もう一つの関数を定義することです.
 function f1(){

    n=999;

    function f2(){
      alert(n); // 999
    }

  }
作用するドメインチェーンの規則によって、下の作用域は声明の変数がなくて、上の1級に探して、探し出せないで帰って、ずっと探して、windowの変数まで、undefinedに帰りません.上のコードでは、関数f 2が関数f 1の内部に含まれており、f 1の内部のすべての局所変数は、f 2に対して可視である.しかし、逆にだめです.f 2内部の局部変数は、f 1に対しては見えません.これはJavascript言語特有の「連鎖作用領域」構造であり、
サブオブジェクトは、すべての親オブジェクトの変数を1段階ずつ上に探します.したがって、親オブジェクトのすべての変数は、子オブジェクトに対しては見えますが、逆は成立しません.
f 2は、f 1の局所変数を読み取ることができるので、f 2を戻り値とすると、f 1の外部で内部変数を読み出すことができるのではないでしょうか?
function f1(){

    n=999;

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999
以上のコードは実際には閉じられています.
3.クローズドの理解はJavascript言語では関数内部のサブ関数のみが局所変数を読み取ることができるので、クローズドを簡単に「関数の内部に定義された関数」と理解することができます.
したがって、本質的には、クローズドとは、関数の内部と関数の外部をつなぐ橋のことである.
4.クローズドの用途クローズドは実際には二つの用途があります.一つは前述の関数内部の変数を読み取ることができます.もう一つはこれらの変数の値を常にメモリに保存させます.
クローズドが変数を常にメモリに保持できるようにするにはどうすればいいですか?
function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000
このコードの中で、resultは実はクローズドf 2関数です.全部で二回運行しました.一回目の値は999、二回目の値は1000です.これは、関数f 1の局所変数nがメモリに保存されており、f 1呼び出し後に自動的に消去されないことを証明している.
どうしてですか?f 1はf 2の親関数であり、f 2はグローバル変数を与えられ、f 2は常にメモリにあり、f 2の存在はf 1に依存しているため、f 1も常にメモリにあり、呼び出しが終了した後、ゴミ回収メカニズムによって回収されることはない.
このコードのもう一つの注目すべき点は、「nAdd=function(){n+=1]」という行であり、まずnAddの前にvarキーワードが使用されていないため、nAddは局所変数ではなくグローバル変数である.第二に、nAddの値は匿名関数であり、この匿名関数自体もクローズドであるため、nAddは相当であり、関数の外部で関数内部の局所変数を操作することができる.
5.クローズドパックを使うには注意が必要です.1)クローズドを濫用するとメモリが漏れてしまいます.クローズドは関数の変数がメモリに保存されていますので、メモリの消耗が大きいので、クローズドを乱用してはいけません.そうでないと、ウェブページの性能問題が発生し、IEで内部保存漏れを引き起こす可能性があります.解決方法は、関数を終了する前に使用しないローカル変数をすべて削除します.
2)親関数の内部変数の値が変わります.したがって、親関数を対象として使用する場合は、クローズドをそのパブリックメソッドとして、内部変数をそのプライベート属性として扱い、親関数内部変数の値を勝手に変えないように注意してください.
6.次のいくつかのよくある例を見て、クローズドに対する理解を深めます.1.次のコードの中には対象があります.関数ではなく、クローズドではありません.
var name = "The Window";   
  var object = {   
    name : "My Object",   
    getNameFunc : function(){   
      return function(){   
        return this.name;   
     };   
    }   
};   
alert(object.getNameFunc()());  //The Window
2.
function outerFun()
 {
  var a=0;
  function innerFun()
  {
   a++;
   alert(a);
  }    
 }
innerFun()
以上のコードはエラーです.innerFun()の作用領域はouterFun()の内部にあり、outerFun()の外部呼び出しはエラーです.このように修正してもいいです.
function outerFun()
    {
         var a=0;
         function innerFun()
         {
              a++;
              alert(a);
         }
     return innerFun;  //    
}
var obj=outerFun();
obj();  //   1
obj();  //   2
3.もう一つの例を見に来ます.
function outerFun()
{
 var a =0;
 alert(a);  
}
var a=4;
outerFun();
alert(a);
結果は、0,4.関数の内部でvarキーワードのメンテナンスaを使用していますので、outFun()の内部にあります.次のコードを見てください.
function outerFun()
{
 //  var 
 a =0;
 alert(a);  
}
var a=4;
outerFun();
alert(a);
その結果、0,0作用ドメインチェーンは、経路に沿って変数の値を決定することができる経路を記述する用語であり、a=0を実行すると、varキーワードが使用されていないため、値付け動作は、作用領域チェーンに沿ってvar a=4になる.を選択し、値を変更します
より深いレベルの理解クローズドは、リンクを参照することができます.https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
このブログは阮一峰先生が説明した閉包内容についての個人的な理解を見ました.