TIL 15|エンクロージャ、this


エンクロージャ


コンセプト

function outerFunc() {
  var x = 10;
  var innerFunc = function() {console.log(x)};
  innerFunc();
}

outerFunc(); // 10
スキャンは、関数を呼び出すときではなく、宣言時に決定されます.(Lexical Scopeng)innerFuncの親ScopeはouterFuncです.
innerFunc関数を呼び出し、まずinnerFunc内部で変数xを検索します.ないので、OuterFuncの変数xをスキャンチェーンに沿って参照し、出力値は10です.
OuterFuncがinnerFuncを呼び出さずに戻ったら?
function outerFunc() {
  var x = 10;
  var innerFunc = function() {console.log(x)};
  return innerFunc();
}

var inner = outerFunc();
inner(); // 10
OuterFuncはinnerFuncを返し、呼び出しスタックから削除します.しかし、グローバル呼び出しの内部関数は、スタックから削除された外部Func内部変数を参照します.
このように,外部関数の外調に内部関数を用いても,外部関数の領域変数にアクセスできる関数をclosureと呼ぶ.自己生成時の環境(LexicalEnvironment)を記憶する関数といえる.

活用する


これらのデータを操作する関数に一部のデータを関連付けることができるため、非常に役立ちます.(javascriptを使用したオブジェクト向けプログラミング)ディスクキャビネットを使用して、不変性と付随効果を最大限に抑制します.

保持状態


    var box = document.querySelector('.box');
    var toggleBtn = document.querySelector('.toggle');

    var toggle = (function () {
      var isShow = false;

      // ① 클로저를 반환
      return function () {
        box.style.display = isShow ? 'block' : 'none';
        // ③ 상태 변경
        isShow = !isShow;
      };
    })();

    // ② 이벤트 프로퍼티에 클로저를 할당
    toggleBtn.onclick = toggle;
  
スイッチング関数はインスタント実行関数であり,スイッチングが返す匿名関数は変数isshowを記憶するモジュールである.スイッチング関数はすぐに実行され、呼び出しスタックから消えますが、変数isshowはモジュールによって参照され、有効な変更と最新の変更を維持し続けます.

グローバル変数の無効化


グローバル変数は、誰もがアクセスして変更できるため、エラーが発生しやすいです.モジュールを使用すると、グローバル変数の使用を抑制できます.
   var incleaseBtn = document.getElementById('inclease');
   var count = document.getElementById('count');

    var increase = (function () {
      // 카운트 상태를 유지하기 위한 자유 변수
      var counter = 0;
      // 클로저를 반환
      return function () {
        return ++counter;
      };
    }());

    incleaseBtn.onclick = function () {
      count.innerHTML = increase();
    };
increateはインスタント実行関数であるため、カウンタが0に初期化され続ける場合、匿名関数はカウンタ変数にアクセスし、カウンタを増加させることができる.カウンタ変数は外部からアクセスできないため、グローバル変数のように予期せぬ変更を心配する必要はありません.

情報を隠す

 function Counter() {
  // 카운트를 유지하기 위한 자유 변수
  var counter = 0;

  // 클로저
  this.increase = function () {
    return ++counter;
  };

  // 클로저
  this.decrease = function () {
    return --counter;
  };
}

const counter = new Counter();

console.log(counter.increase()); // 1
console.log(counter.decrease()); // 0
コンストラクション関数によって作成されるオブジェクトのメソッドは、オブジェクトのプロパティだけでなく、自分の記憶にあるLexical環境変数にもアクセスできます.したがって、増減は共通変数カウンタにアクセスおよび変更できます.Closerクラス言語ベースのプライベートキー機能を使用できます.
*ただし、適切に使用しないと、パフォーマンスの問題やメモリの問題が発生することに注意してください.

this


C++のthis(自分はインスタンス化されたオブジェクト)と同じ名前ですが、同じ概念ではありません.(クラスの構文としても使用できます...)
この値のほとんどは、関数を呼び出す方法によって決まります.ES 5はbind法を導入し、明示的なバインディングを可能にした.ES 2015の矢印関数はこのバインディングを提供せず、ディレクトリコンテキストのこの値を維持した.

さぎょうモード


デフォルトのバインド


コンテキストオブジェクトとしてグローバルオブジェクト(ウィンドウ)を使用します.(グローバル実行コンテキスト)グローバルコンテキストでは、厳格なモードにあるかどうかにかかわらず、プリアンブルオブジェクト(window)が参照されます.

暗黙的バインド


オブジェクトから関数を呼び出すと、メソッドを呼び出すオブジェクトがメソッドのコンテキストオブジェクトになります.厳密モードでは、関数がこの値を明示的に設定していない場合は、定義されていません.
function test() {
  console.log(this.a);
}

var obj = {
  a: 20,
  func1: test,
  func2: function() {
    console.log(this.a);
  }
};

obj.func1(); // 20
obj.func2(); // 20

明示的なバインド


呼び出し方法に関係なく、関数オブジェクトをcall、apply、bindメソッドで最初のパラメータとして宣言すると、コンテキストオブジェクトになります.

新しいバインド

function foo(a) {
  this.a = a;
  this.qwer = 20;
}

var bar1 = new foo(2);
console.log(bar1.a); // 2
console.log(bar1.qwer); // 20

// 1. 새 객체가 만들어짐
var obj = {};
// 2. 새로 생성된 객체의 Prototype 체인이 함수의 프로토타입과 연결됨
Object.setPrototypeOf(obj, foo.prototype); // 프로토타입을 연결합니다. 이 글에서는 무시해도 상관없습니다.
// 3. 1에서 생성된 객체를 context 객체로 사용(명시적으로)하여 함수가 실행됨
foo.call(obj, 2);
// 4. 이 함수가 객체를 반환하지 않는 한 1에서 생성된 객체가 반환됨
var bar2 = obj; // 여기서 foo는 반환(return)이 없으므로 인스턴스가 생성(된 것처럼 동작)

console.log(bar2.a); // 2
console.log(bar2.qwer); // 20

矢印関数のバインド


矢印関数宣言部のScopeこのコンテキストが矢印関数のこのコンテキストになります.
var obj = {
    a: this, // 2. 일반적인 경우 this는 window,
    b: function() {
      console.log(this) // 3. 메소드의 경우 this는 객체
    },
    c: () => {
        console.log(this)
        // 4. 화살표 함수의 경우 정적 컨텍스트를 가짐, 함수를 호출하는 것과 this는 연관이 없음
        // 5. 따라서 화살표 함수가 정의된 obj 객체의 this를 바인딩하므로 this는 window
    }

}

obj.b() // 6. obj
obj.c() // 7. window

Reference

  • モジュール-モダンJavaScript Deep Dive
  • MDN - this
  • JavaScript thisの4つの操作方法