TIL 76|JS Closure(2)使用と注意事項


なぜCloser(1)はCloserなのですか?
*利用情報公開
最初に出現したモジュールの概念から見ると、JavaScriptのすべての関数はモジュールである.なぜなら、関数を作成し、関数内部のコードナビゲーションのスキャンを関数作成時の語彙scopeに固定すると、モジュールになるからです.
しかし,実際にはすべてのJavaScript関数をCloserと呼ぶわけではない.
通常、外部アクセスを制限する専用変数が環境に存在する場合は、エンクロージャと呼ばれます.この場合、下一篇に記載のエンクロージャを使用する理由は正しい.
エンクロージャの用途の要約:情報の非表示とパッケージング
1.キャビネットで非表示
1-1. IIFEの使用
var counter = (function() {
  var privateCounter = 0;function changeBy(val) {    │ increment, decrement, value
    privateCounter += val;    │ 세 메소드가 공유하는 private items
  }return {
    increment: function() {   
      changeBy(1);	      
    },		
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1
コードの説明
匿名関数はすぐに実行され、counter変数に関数の戻り値が割り当てられます.
匿名関数の戻り値である3つの関数は、同じ語彙環境を共有し、privateCounterおよびchangeBy()にアクセスできる唯一のモジュールである.
  • 共通関数=特権メソッド=公開メンバーcounter.increment counter.decrement counter.value
  • 非公開メンバーprivateCounter changeBy
  • 1-2. 記名関数の使用
    IIFEではなく記名関数を使用する場合、独立性を維持する複数のカウンタを生成することができる.
    var makeCounter = function() {
      var privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      }
    };
    
    var counter1 = makeCounter();
    var counter2 = makeCounter();
    alert(counter1.value()); /* 0 */
    counter1.increment();
    counter1.increment();
    alert(counter1.value()); /* 2 */
    counter1.decrement();
    alert(counter1.value()); /* 1 */ ┐ counter1과 counter2는
    alert(counter2.value()); /* 0 */ ┘ 서로 다른 독립된 클로저를 가진다.
    コードの説明
    各モジュールは、異なるバージョンのprivateCounter変数(独自のモジュールを介して)を参照します.
    1つのモジュールで変数値を変更しても、他のモジュールの値には影響しません.
    2.Curryingによるテンプレート化
    Curryingは、複数のパラメータを受信する関数を、単一のパラメータを受信する関数チェーンを使用する方法に変換することを意味する.記名関数を使用すると、上記の例のcurryingを組み合わせて、Closer関数の外部関数をテンプレートとして使用できます.
    function greetCurried (greeting) {
      return function (name) {
        console.log(greeting + ", " + name);
      }
    }
    
    greetCurried("Hi there")("Howard"); //"Hi there, Howard"
    
    let greetHello = greetCurried("Hello");
    greetHello("Heidi"); //"Hello, Heidi"
    greetHello("Eddie"); //"Hello, Eddie"
    
    
    let greetGoodmorning = greetCurried("Good morning");
    
    greetGoodmorning('wendy');	 // Good morning, wendy
    greetGoodmorning('honey'); // Good morning, honey
    *使用上の注意
    パフォーマンスとメモリの問題
  • エンクロージャは、メキシコ環境を記憶するためにメモリを消費します.GCは内部変数によって消費されるメモリを自動的に回収しないため、モジュールを使用した後、参照を削除し、JavaScriptがメモリを回収できることを通知し、メモリの浪費を避ける必要があります.
  • scopeチェーンを遡る行為があるので、少し遅いです.
  • 反復文モジュール
    キャビネットの理解が不十分な場合は、文を繰り返してキャビネットと一緒に使用すると、予想とは異なる結果が得られます.下一篇で最初に提起された問題を再びもたらした.
    function count() {
        var i;
        for (i = 0; i < 5; i++) {
          console.log(i)
            setTimeout(function() {
                console.log(i)
            }, i*1000)
        }
    }
    count();
    作者の意図💭 : 0、1、2、3、4は1秒間隔で出力されます!
    実績出力結果:5、5、5、5、5
    settimeoutだけでなく,イベントリスナーなどの繰り返し文ループ後に実行できるcloser関数にも注意する.コードを予想通りに動作させるには、次の方法で解決します.
    ソリューションIIFE + parameter
    n/a原理
    settimeout関数の外に新しいscopeを作成し、IIFEでiをパラメータとして渡します.iを増加させるごとに、固定されたcountingNumberとは独立したCloser関数が生成される.
    独立した語彙環境を有し、各時点のcountingNumberにアクセスして出力することができる.
     function count() {
         var i;
         for (i = 0; i < 5; i++) {
             (function(countingNumber) {
                 setTimeout(function() {
                console.log(countingNumber)
            }, i*1000)
             })(i);
         }
     }
     count(); //0,1,2,3,4
    (+)settimeoutでなければ、新しいスキャンを作成せず、IIFEを適用するだけで問題を解決できます.ただし、settimeoutは、コールバック伝達の関数自体にIIFEを適用すると、2番目のパラメータとしての遅延は機能しないため、外部に新しいスキャンを作成して適用するしかありません.
    ソリューションES 6に追加されたBlock Scope
    n/a原理
    varには関数レベルがあり、letにはブロックレベルのスキャンがあります.
    すなわちletキーワードを使用するブロックに対してlexicalenvironmentが再生成される.
    繰り返し文は実行するたびにモジュールを生成するため、iは各時点のiにアクセスし、増加した数値を出力することができる.
    function count() {
        'use strict';
        for (let i = 0; i < 5; i++) {
            setTimeout(function timer() {
                console.log(i);
            }, i*1000);
        }
    }
    count(); //0,1,2,3,4
    参考資料
    JavaScriptモジュール(Close)
    JavaScript/スキャン、クローズ、インスタント実行関数の学習(IIFE)