エンクロージャ


Closure


1.Closerとは?


外部関数の変数の内部関数またはこの動作原理にアクセスできます.
関数と宣言関数の語彙環境の組合せ
ex)
function outerFn() {
let outerVar = 'outer';
console.log(outerVar);
function innerFn() {
let innerVar = 'inner';
console.log(innerVar);
}
return innerFn;
}
let globalVar = 'global';
let innerFn = outerFn();
innerFn();
そういえば、Closer関数は黄色です.

2.Closer関数の特徴


Closer関数では、領域変数、外部関数の変数、およびグローバル変数にアクセスできます.

3.Closer入門


(質問1)次のコードで内部関数から何個のScopeにアクセスできますか?
function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);
  
  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
}

let globalVar = 'global';
outerFn();
答えは全部で3つあります.
1 .
  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
2 .
function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);
  
  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
}
3 .
function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);
  
  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
}

let globalVar = 'global';
outerFn();
scopeはここから始まります.
(質問2)
もし、もし
function outerFn() {
let outerVar = 'outer';
console.log(outerVar);
function innerFn() {
let innerVar = 'inner';
console.log(innerVar);
}
return innerFn;
}
outerFn();
このようにreturn innerFn;を追加すると、outerFn()の実行結果は何ですか?
この場合、関数も返すことができます.

それでは次の問題もやりましょう
(問題3)
function outerFn() {
  let outerVar = 'outer';
  console.log(outerVar);
  
  function innerFn() {
    let innerVar = 'inner';
    console.log(innerVar);
  }
  return innerFn;
}
この場合、コンソールで次のような状況が発生します.
outerFn()();//①
let innerFn = outerFn();//②
innerFn();//③

4.有用なエンクロージャの例


1)フック:n個の関数を作成し、各関数にn個のパラメータを受信させ、1個の関数ではなくn個のパラメータを受信させる。

function adder(x) {
  return function(y) {
    return x + y;
  }
}

이라고 하면 
adder(2)(3); // 5 : 2 + 3

let add100 = adder(100); 이라고하면
add100(2); // 102 : 100 + 2
add100(10); // 110 : 100 + 10

let add5 = adder(5); 라고 하면
add5(2); // 7 : 5 + 2
xの値を固定して再使用できます!

2)外部関数の変数は保存されており、テンプレート関数とともに使用可能

function htmlMaker(tag) {
  let startTag = '<' + tag + '>';
  let endTag = '</' + tag + '>';
  return function(content) {
    return startTag + content + endTag;
   }
 }
 
 let divMaker = htmlMaker('div');
 divMaker('code'); // <div>code</div>
 divMaker('states'); // <div>states</div>
 
 let h1Maker = htmlMaker('h1');
 h1Maker('Headline'); // <h1>Headline</h1>

3)キャビネットモジュールモード:変数を鏡の中に置いて、関数の外に露出しないようにする方法

function makeCounter() {
  let privateCounter = 0;
  
  return {
    increment: function() {
      privateCounter++;
    },
    decrement: function() {
      privateCounter--;
    },
    getValue: function() {
      return privateCounter;
    }
  }
}
もしそうなら、
let counter1 = makeCounter();
counter1.increment();
counter1.increment();
counter1.getValue();

let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue();
もしそうなら、counter1.getValue();counter2.getValue();の値は何ですか?
答えは.
let counter1 = makeCounter();
counter1.increment();
counter1.increment();
counter1.getValue(); // 2

let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue(); // 1
はい.
上記の答えのように、2つのカウンターで異なるprivateCounterをそれぞれ操作し、privateCounterを外に露出させません.