JavaScript関数クローズド(colsure)

6700 ワード

クローズドを理解するには、まずJSの変数スコープ、JavaScriptのスコープとスコープチェーンを理解しなければなりません.
ES 6の前に、変数のスコープはグローバルスコープとローカルスコープに分けられます.関数内部では大域変数を直接読み取ることができますが、一方では関数の外部では関数内の局所変数を読み取ることができません.
1.では、問題が来ました.外部からローカル変数をどのように読み取りますか?
関数内部の関数は親関数にアクセスできる変数であることを知っています.
function patty(){
   var sum=666;
   function pattySon(){ 
     alert(sum);
   }
}
上のコードの中で、関数のpatty Sonは関数のpattyの内部に含まれています.この時、キティちゃんの内部のすべての局所変数は、キティSonに対して見られます.しかし、逆は成り立たない.これはJavascript言語特有の「連鎖作用領域」構造であり、サブオブジェクトは一段ずつ上に向かってすべての親オブジェクトの変数を探します.したがって、親オブジェクトのすべての変数は、子オブジェクトに対しては見えますが、逆は成立しません.キティSonがキティちゃんの内部変数にアクセスできるなら、私たちはキティSonに戻るだけで、外部からキティちゃんの中の局部変数にアクセスできます.
function patty(){
   var sum=666;
   function pattySon(){ 
     alert(sum);
    }
   return pattySon;
}

var res=patty();
res();    //666
2.クローズドとは何ですか
簡単に言えば、クローズドは親関数の局所変数にアクセスする関数です.例えば、親関数の関数または変数をグローバル変数、またはグローバルオブジェクトの属性に指定します.あるいは直接にこの内部関数を返します.
3.クローズドの用途
クローズドは多くのところで使えます.その最大の用途は二つあります.一つは前述の , 、もう一つは , ( , )です.
実際には、クローズドパックを使うことによって、多くのことができます.(1)結果キャッシュは、処理に時間がかかる関数オブジェクトがあると仮定しています.計算された値を記憶しておく必要があります.この関数を呼び出すと、まずキャッシュで調べてみます.見つけられなかったら、計算してキャッシュを更新し、値を返します.見つけたら、検索した値をそのまま返します.この点は、外部からの参照を解放しないので、関数内部の値を保持することができます. もクローズドの原理を利用しています.
var CachedSearchBox = (function(){    
    var cache = {},    
       count = [];    
    return {    
       attachSearchBox : function(dsid){    
           if(dsid in cache){//            
              return cache[dsid];//              
           }    
           var fsb = new uikit.webctrl.SearchBox(dsid);//      
           cache[dsid] = fsb;//        
           if(count.length > 100){//       <=100    
              delete cache[count.shift()];    
           }    
           return fsb;          
       },    
     
       clearSearchBox : function(dsid){    
           if(dsid in cache){    
              cache[dsid].clearSelection();      
           }    
       }    
    };    
})();    
     
CachedSearchBox.attachSearchBox("input");
(2)パッケージソース
(function() {   
   var _userId = 23492;   
   var _typeId = 'item';    
   var export = {}; 
    
   function converter(userId) {          
     return +userId; 
   } 
    export.getUserId = function() {         
       return converter(_userId);     
   } 
   export.getTypeId = function() {          
      return _typeId; 
   }         
   window.export = export;   //       
}());
  export.getUserId(); // 23492 
  export.getTypeId();  // item 
  export._userId;    // undefined  
  export._typeId;    // undefined       
  export.converter; // undefined

クローズドの特性を利用して、いくつかの複雑な関数論理をカプセル化できます.この例では、export上の方法getUserIdを呼び出して、getTypeIdは間接的に関数内の変数を実行します.これもNodeの中でよく使われている特性でしょう.また各種jsライブラリのように、ソースコードを封入します.jQueryを例にとって
function (window, undefined){
    //  jQeury  
        var jQuery = function(selector, context){
            return new jQuery.fn.init(selector, context);
        }

        jQuery.fn = jQuery.prototype = {
            //      ,   jQuery             
            init:function(selector, context){
           }
        }

        jQuery.fn.init.prototype = jQuery.fn;
        window.jQeury = window.$ = jQuery;
    }();
})(window);
(3)クラスの実現(プライベート変数のシミュレーション)と継承
//   
function Person(){    
    var name = "default";       
       
    return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
    };   

    var p = new Person();
    p.setName("Tom");
    alert(p.getName());

   //  
    var Tom= function(){};
    //   Person
    Tom.prototype = new Person();
    //      
    Tom.prototype.Say = function(){
        alert("Hello,my name is Tom");
    };
    var a = new Tom();
    Tom.setName("Tom");
   Tom.Say();
    alert(Tom.getName());
私たちはPersonを定義しました.これはカテゴリのように、私たちnewのPersonオブジェクトを訪問する方法です.Tomを定義し、Personを継承し、自分の方法を追加します.
4.クローズドを使う注意点
(1)クローズドは関数の変数がメモリに保存されるため、メモリの消耗が大きいので、クローズドを濫用してはいけません.そうでないと、ウェブページの性能問題が発生し、IEでメモリのリークを引き起こす可能性があります.解決方法は、fn=null // , 。(2)クローズドは親関数の外部にあり、親関数の内部変数の値を変更することである.したがって、親関数を対象として使用する場合は、クローズドをそのパブリックメソッドとして、内部変数をそのプライベート属性として扱い、親関数内部変数の値を勝手に変えないように注意してください.
5.クローズドされた問題を解決するための利器——匿名の自己実行関数
すぐに関数式を実行します.他に名前があります.自動実行匿名関数(self-executing anonymous function).このIIIIFEに接触したのは、最初にクローズド時の問題を解決するためです.
  • クローズド中の循環問題を解決するためによくあるエラーがサイクル中に現れてクローズドパケットを使用する場合、1 s毎の出力サイクルで循環番号
  • を呼び出す必要があると仮定しています.
    for(var i = 0; i < 10; i++) {
        setTimeout(function() {
            console.log(i);  
        }, 1000*i);
    }
    
    上のコードは数字0から9ではなく、1 sごとに10回の数字を出力します.consone.logが呼び出されたとき、forサイクルはすでに終了し、iの値は10に修正された.この時にsetTimeoutの5つのコールバック関数を実行します.中のconsolie.log(i).のi行き先を探して、windowの下のiを探し当てることしかできなくて、つまり10.出力は全部10です.循環番号を正確に取得するためには、私達は人のためにconsolie.log(i)を与える必要があります.スコープを作る
    (1)自己実行匿名関数を使用する.
    for(var i = 0; i < 10; i++) {
        (function(e) {
            setTimeout(function() {
                console.log(e);  
            }, e*1000);
        })(i);
    }
    
    (2)bindを使用する
    for(var i=0;i<7;i++){
      setTimeout(function(i){console.log(i)}.bind(null,i),i*1000)
    }
    
    (3)もちろんもっと簡潔な方法があります.ES 6のletキーワードを使います.
    for (let i = 0; i < 10; i++) {   //let    var
      setTimeout(function (){
        console.log(i); 
       },i*1000); 
    }
    
    letはコードブロックの作用領域であるため、forサイクル毎に、consolone.log(i);forコードブロックの作用領域のiに引用されているので、forループが終了した後、これらの作用領域はsetTimeoutが実行されない前に解放されない.
    分からないなら、ここを見てください.http://es6.ruanyifeng.com/#docs/let#
    IIIFのその他の用途
  • は、グローバル変数の汚染を避けるために、すべての変数を知っています.varキーを追加しないと、デフォルトはグローバルオブジェクトの属性に追加されます.このような一時変数は、グローバルオブジェクトに参加することに多くのデメリットがあります.例えば、他の関数はこれらの変数を誤用する可能性があります.全体のオブジェクトが大きすぎて、アクセス速度に影響を与えます.
  • 現在のイベントを「2017/1/1 0:10:24」という形で変数に割り当てる必要があります.しかも一回だけ行う必要があります.すぐに実行関数だけを選択します.
    var currentTimr=(function(){
       var time=new Date();
       var year=time.getFullYear();
       var month=time.getMonth();
       var date=time.getDtate();
       var hour=time.getHours();
       var min=time.getMinutes();
       var sec=time.getSeconds();
       return year+'/'+month+'/'+date+'  '+hour+':'+min+':'+sec;
    })()
    
    同じようにカバンの衝突を防ぐことができます.
    a.js var num=1; b.js var num=2;ページでa.jsとlb.jsの2つのライブラリを同時に参照すると、num変数が上書きされるのは必至です.
    a.js
       (function(){
           var num=1;
           //code...
       })();
    
    b.js
    (function(){
       var num=2; 
      //code...
     })();