Closure - (a)


A Closure is the combination of a function and the lexical environment within which that function was declared
モジュール-Javascript|MDN
MDN対モジュールは以下のように定義される.正義はあまりにも難解で,触れにくい.例を使用して、エンクロージャを全面的に理解します.
const x = 1;

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

outerFunc();
outerFunc関数の内部でネストされた関数innerFuncを定義して呼び出します.このときinnerFuncの親(親)ミラーは外部関数の親(親)ミラーです.
したがって,innerFunc内部からは,独自の外部関数(親)外部Funcを含むx変数にアクセスできる.(自分の親鏡にアクセスできる変数として理解される)
InnerFunc関数がouterFunc内部で定義されたオーバーラップ関数でない場合(外部で別の宣言された関数である場合)、outerFunc関数の内部からinnerFunc関数を呼び出してもouterFunc関数の変数にアクセスできません.
const x = 1;

function outerFunc () {
  const x = 10;
  innerFunc();
}

function innerFunc () {
  console.log(x); // 1
}

outerFunc();
これはjavascriptがLexicalScopeに従う言語だからです.
(上記の例はあまりにも当たり前です)

でんしけんびきょう


レクシー・カルスコフについては、以前勉強したことがあります.実行コンテキストをLexical Scoperの観点から見てみましょう.
JAvascriptエンジンは、関数がどこから呼び出されるかではなく、関数定義に基づいて親スキャンを決定します.これを静的走査と呼ぶ.
=>上記の例では、外部Func内部からinnerFuncが呼び出されていますが、innerFunc定義の場所が全く異なるため(外部)、他の変数は参照できません.
const x = 1;

function foo () {
  const x = 10;
  bar();
}

function bar () {
  console.log(x);
}

foo(); // (1) ??
bar(); // (2) ??
(1) = 1, (2) = 1
呼び出しfoo関数->foo関数のscopeでxが10に割り当てられ、呼び出しbar関数->コンソールでxが撮影されるとxはグローバルscopeの1(10に割り当てられたxはfoo関数の領域scopeにのみ適用されるxであるため)->コンソールが1に撮られ、foo関数が終了したらbar関数を呼び出す->上記の理由に基づいてグローバル変数xの値1を出力する
もう一度言いますが、
関数の親スキャンがどこで呼び出されたのか、どこで定義されたのかを知っていれば、後の内容を理解するのは難しくありません.
=>foo関数とbar関数の親scopeはグローバルです.
実行コンテキストで学習したように、Scopeのエンティティは実行コンテキストの集合環境です.このクラスタ環境は、「外部クラスタ環境への参照」によって親クラスタ環境に関連付けられます.これがスコフチェーンです.
関数によって定義される環境(場所)と呼び出される環境(場所)が異なる場合があります.したがって、LexicalScopeを可能にするには、定義された環境、すなわち定義された環境(関数定義が存在するScopeが親Scope)を覚えておく必要があります.
このため、関数は内部スロット[Environment]に自分で定義した環境、すなわち親ミラーの参照を格納します.
すなわち、関数定義が評価され、関数オブジェクトが生成されると、定義された環境(位置)によって決定された親スキャンの参照が、関数オブジェクト自体の内部スロットに格納されます.
このとき、内部スロットに格納されている親スキャンプログラムの参照は、現在実行中の実行コンテキストのディレクトリ環境を指します.なぜなら、関数定義が評価され、関数オブジェクトが生成される時点は、関数定義の環境、すなわち親関数(またはグローバルコード)が評価または実行中である時点であり、実行される実行コンテキストは親関数の実行コンテキストであるからである.
要約すると、関数オブジェクトを作成すると、親スキャンの参照が自分の場所に格納されます.これが現在実行中の実行コンテキストです.親関数が計算または実行されると、その関数オブジェクトが作成されます.
したがって、関数オブジェクトの内部スロットに格納されている現在実行中の実行コンテキストの集合環境の参照が親ミラーです.
これは、自身を呼び出したときに生成される関数集合環境の「外部集合環境への参照」に格納される参照値でもあります.関数オブジェクトは、内部スロットに格納されているディレクトリ環境の参照を記憶します.つまり、それ自体が存在する限り、親スキャンを記憶できます.
(今はだんだん難しくなってきました…)
上記の例では、bar関数は、自分の親scope(すなわちグローバルクラスタ環境)を内部スロットに格納することによって記憶される.

エンクロージャとリモート環境

const x = 1;


function outer() {  // (1)
  const x = 10;
  const inner = function () {
    console.log(x); // (2)
  }
  return inner;
}

// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer(); // (3)
innerFunc(); // (4) 10
??????
私の頭の中で:どうして10がありますか?明らかにouter関数は内層関数を返して死んでしまうので,当然outerの領域走査におけるx=10も消失する.
これがCloserの概念です.ゆっくり理解しましょう.
outer関数(3)を呼び出し、outer関数はオーバーラップ関数の内部を返し、ライフサイクルを終了します.すなわち、outer関数の実行が終了すると、outer関数の実行コンテキストが実行コンテキストスタックから除去される(pop).
このとき、領域変数xと変数値10を格納するouter関数の実行コンテキストが削除され、outer関数の領域変数xもライフサイクルを終了する.したがってouter関数の領域変数xは有効ではないため、x変数にアクセスできません.ないことはない
ここまで来るなんて、当たり前ですね.明快無比.
ただし、上記コードの実行結果(4)はouter関数の領域変数xの値10である.ライフサイクルが終了し、実行コンテキストスタックから削除されたouter関数の領域変数xが再び復活しました.
△別のスペースに用意しますか?念のため?別に保存したのではないでしょうか.
オーバーラップ関数の時間が外部関数より長い場合、オーバーラップ関数はライフサイクルが終了した外部関数を参照できます.
これらの重ね合わせ関数を「閉じる」と呼びます.
お腹が空いていたのでしばし休憩.

To be continue..