レプリカ


モジュールについては、詳細については、コンテキストとスキャンチェーンの実行に記載されている[[scope]]propertyを参照してください.😆

Closerとは?


エンクロージャとは?🤔
簡単に言えば、実行済みの外部関数の変数にアクセスできる関数と言える.
次のコードを見てから確認しましょう.
var outer = function(){
    var x = 5;
    
    var inner = function(){
        console.log(x);
    };

    inner();
};

outer(); // 5
上記のコードのように,スキャンチェーンを介して内部関数から外部関数にアクセスできる変数が知られている.
では、コードを交換します.
var outer = function(){
    var x = 5;
    
    var inner = function(){
        console.log(x);
    };

    return inner;
};

var func = outer();

func(); // 5
今回,outer()関数はinner()関数を呼び出さず,inner()関数を返して変数に代入して実行したが,同じ結果を出力した.
実行コンテキストの構造を考慮すると、outer()関数の実行コンテキストは呼び出しが終了するとスタックから消え、outer()関数の変数にどのようにアクセスしますか...?🙄
これは、関数オブジェクトが持つ内部プロセス[[scope]]であり、関数は宣言位置に基づいて作成されたスキャンチェーンによって外部関数の変数を参照するため、「ゴミ収集」のターゲットにならないためである.
実行済みの外部関数の変数にアクセスできる関数をcloser,closerが参照する外部関数の変数を自由変数と呼ぶ.
では、次の状況で何が起こるか見てみましょう.😉
var x = 10;

var outer = function(){
    var x = 5;
    
    var inner = function(){
        console.log(x);
    };

    return inner;
};

var func = outer();

func(); // 5
今回は、グローバル変数としてvar x = 10をグローバルオブジェクトに宣言します.しかし,func()関数の出力結果は,以前と同様5であることが分かる.func()関数もグローバル変数ですが、なぜxをグローバルコンテキストで参照せず、outer() 실행 컨텍스트x値を参照しないのですか?
なぜなら、キャビネットも電子顕微鏡を利用して自分の主張する位置で上位鏡を決定するからだ.

利用モジュール


何のモジュールか知ってる...それはどこに使うやつですか.例を見てみましょう.😆

1.グローバル変数の使用を最小化


グローバル変数の宣言が多ければ多いほど、エラーが発生し、敵と同じように存在します.
エンクロージャを使用すると、これらのグローバル変数の使用を最小限に抑えることができます.次のコードを見て理解してください.🙂
var change = (function(){
    var bool = false;

    return function(){
        bool = !bool;
        return bool;
    };
})();

console.log(change()); // true
console.log(change()); // false
console.log(change()); // true
即時実行関数boolを使用して、変数をグローバル変数として宣言せずに値を維持し、呼び出しによって反転することがわかります.

2.情報を隠す


モジュールを使用すると、Javaのアクセス制御プログラムprivateを使用するように、関数オブジェクトの外部から内部変数にアクセスすることを防止できます.コードを見て理解しましょう.🤗
function Func(){
    var num = 1;

    this.plus = function(){
        num++;
    };

    this.minus = function(){
        num--;
    };

    this.print = function(){
        console.log(num);
    };
};

var obj = new Func();

obj.plus(); // num === 2
obj.plus(); // num === 3
obj.minus(); // num === 2
obj.print(); // 2
コンストラクション関数Func()numは非Property変数であるため、外部からアクセスすることはできません.次のplus()minus()print()関数を使用してアクセスする必要があります.
しかし、これだけのメリットがあるCloserもメリットだけではありません...

エンクロージャ使用時の注意事項


1.繰り返し文を使用する場合


Closerの使用上の注意点といえばよく出てくるやつ!繰り返し文のCloserを使用します.問題を引き起こすコードから見ましょう!
var arr = [];

var func = function(){
    for(var i = 0; i < 5; i++){
        arr[i] = function(){
            console.log(i);
        };
    }
}

func();
arr[0](); // 5
arr[1](); // 5
arr[2](); // 5
arr[3](); // 5
arr[4](); // 5

意図的な動作は、iarr[]indexとともに徐々に増加していくが、すべての出力5を出力しようとする.
なぜなら、arr[i]に含まれる関数は変数iをcloserとして参照し、for 반복문が終了すると、インクリメンタルi++によってiの値が5になるからである.
その後、console.log(i)iの値を出力しようとすると、クローンが重複して値を増やしたiの値が表示され、このような状況が発生します.
これらの問題を解決するには、次の新しいスキャンプログラムを追加します.
var arr = [];

var func = function(){
    for(var i = 0; i < 5; i++){
        arr[i] = (function(x){
            return function(){
                console.log(x);
            };
        })(i);
    }
}

func();
arr[0](); // 0
arr[1](); // 1
arr[2](); // 2
arr[3](); // 3
arr[4](); // 4
関数の外部に新しいscopeが作成され、即時実行関数を使用してiiを伝達パラメータxに変更し、正常に動作します.😆
これは、varキーワードの関数単位スキャンによる現象で、ES6に追加されたletキーワードを使用して、外部に追加のスキャンを作成する必要がなく、簡単に解決できます.

2.メモリ乱用


エンクロージャは常に[[scope]]propertyを使用して外部関数の変数がゴミ収集の目標になることを阻止します.
この言葉は逆に,元のコールが終了すると,返す必要があるメモリがモジュールに保持されているともいえる.
したがって、エンクロージャが切れた場合は、メモリの漏洩を防ぐために、nullをフリー変数を参照する変数に挿入してください.
参考資料
[Closure | PoiemaWeb] https://poiemaweb.com/js-closure