キャビネット(closure)


キャビネット(closure)
:関数(内部関数)のライフサイクルが、宣言された関数のスキャン領域(内部関数が定義されている領域;外部関数)のライフサイクルよりも長い場合、外部関数のライフサイクルが終了した後に内部関数を呼び出すと、外部関数の領域変数にアクセスできます.この関数を閉パッケージと言います.
外部関数が計算されて関数オブジェクトが生成されると、現在実行中の実行コンテキストのディレクトリ環境(グローバルオブジェクトのグローバルディレクトリ環境)が親として内部スロット[Environment]に格納されます.
外部関数を呼び出すと、外部関数の集合環境が作成され、その内部スロット[Environment]に格納されている参照値グローバル集合環境が外部語彙環境に格納されます.このように外部語彙環境に接続される上位集合環境が上位のScopeであり,語彙環境に接続されるこれをScopeチェーンと呼ぶ.
外部関数が呼び出されると、内部関数が評価されます.内部関数オブジェクトが作成され、コンテキストで現在実行されている外部関数の集合環境が内部関数の[環境](Environment)内部スロットで参照されます.(内部関数の位相ミラーは、定義された外部関数のディレクトリ環境になります.)
内部関数を実行する場合、外部関数の実行コンテキストが現在の実行コンテキストスタックに存在しなくても(すなわち、外部関数のライフサイクルが終了する)、内部関数は外部関数のディレクトリ環境を参照できます.これは、外部関数の外部関数の外部環境参照値が呼び出し時に生成される外部関数の外部語彙環境referenceに格納されるためである.したがって、外部関数の実行コンテキストスタックをポップアップした後でも、scopeチェーン(外部ディレクトリ環境への参照に基づいて)に沿ってコンテキストの変数を参照または変更できます.
共有状態変数のモジュールを作成します.
クラスの5人の学生が授業中に質問した回数を計算し、質問回数が多い学生に戻る関数を作成します.
const studentsInfo = (() => {
        const numberOfQuestion = {
          Goofy: 0,
          Judy: 0,
          Sophie: 0,
          Aiden: 0,
          Alex: 0,
        };

        return {
          count: student => {
            numberOfQuestion[student] = numberOfQuestion[student] + 1;

            return numberOfQuestion;
          },
          getTheBestStudent: () => {
            let resultStudent = 'Goofy';
            let resultNum = numberOfQuestion.Goofy;

            for (const student in numberOfQuestion) {
              if (numberOfQuestion[student] > resultNum) {
                resultStudent = student;
                resultNum = numberOfQuestion.student;
              }
            }

            return resultStudent;
          },
        };
      })();

      console.log(studentsInfo.count('Goofy')); // {Goofy: 1, Judy: 0, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(studentsInfo.count('Goofy')); // {Goofy: 2, Judy: 0, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(studentsInfo.count('Judy')); // {Goofy: 2, Judy: 1, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(studentsInfo.count('Judy')); // {Goofy: 2, Judy: 2, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(studentsInfo.count('Judy')); // {Goofy: 2, Judy: 3, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(studentsInfo.count('Aiden')); // {Goofy: 2, Judy: 3, Sophie: 0, Aiden: 1, Alex: 0}
      console.log(studentsInfo.count('Aiden')); // {Goofy: 2, Judy: 3, Sophie: 0, Aiden: 2, Alex: 0}
      console.log(studentsInfo.count('Goofy')); // {Goofy: 3, Judy: 3, Sophie: 0, Aiden: 2, Alex: 0}
      console.log(studentsInfo.getTheBestStudent()); // Goofy
studentsInfo識別子は、オブジェクト内のメソッドカウントおよびgetTheBestStudentを含む、関数を実行して返されるオブジェクトをすぐに割り当てます.両方の方法は、定義されたscopeの即時実行関数内のscopeです(オブジェクトはscopeではないので、即時実行関数を定義したscopeです).独自の位相スキャンチェーンで接続します.したがって、学生情報を持つNumberOfQuestionオブジェクトはcountとgetTheBestStudioでのみクエリーできます.
上記の関数の呼び出し回数は、自身の位相ミラーに存在する識別子を共通に参照するために1回でなければならない.インスタント実行関数を1回だけ呼び出すため、Lexical環境は1回のみ作成され、このように宣言された1つのLexical環境を共有する方法は2つしかないので、一般的なNumberOfQuestionオブジェクトを参照および変更できます.
次の例では、即時実行関数を使用してLexical環境を作成するのではなく、class 1とclass 2でそれぞれ関数を呼び出してオブジェクトを返します.したがって、次のコードには、class 1、class 2という異なるディレクトリ環境を上に2つ構成するモジュールを持つ2つのオブジェクトがあり、次のような異なる結果が表示されます.

      const studentsInfo = () => {
        const numberOfQuestion = {
          Goofy: 0,
          Judy: 0,
          Sophie: 0,
          Aiden: 0,
          Alex: 0,
        };

        return {
          count: student => {
            numberOfQuestion[student] = numberOfQuestion[student] + 1;

            return numberOfQuestion;
          },
          getTheBestStudent: () => {
            let resultStudent = 'Goofy';
            let resultNum = numberOfQuestion.Goofy;

            for (const student in numberOfQuestion) {
              if (numberOfQuestion[student] > resultNum) {
                resultStudent = student;
                resultNum = numberOfQuestion.student;
              }
            }

            return resultStudent;
          },
        };
      };

      const class1 = studentsInfo();
      const class2 = studentsInfo();

      console.log(class1.count('Goofy')); // {Goofy: 1, Judy: 0, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(class1.count('Goofy')); // {Goofy: 2, Judy: 0, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(class1.getTheBestStudent()); // Goofy

      console.log(class2.count('Judy')); // {Goofy: 0, Judy: 1, Sophie: 0, Aiden: 0, Alex: 0}
      console.log(class2.count('Alex')); // {Goofy: 0, Judy: 1, Sophie: 0, Aiden: 0, Alex: 1}
      console.log(class2.getTheBestStudent()); // Judy
したがって、共通の値を使用して何かを行うには、値を使用する関数(モジュール)にディレクトリ環境を共有させる必要があります.そのため、最初の例のように実行関数を使用してすぐに囲む必要があります.