エンクロージャ


JavaScriptで最も曖昧な概念と、もしあなたが新人であれば、面接で最もよく耳にする質問であるCloserについて説明しましょう.
まず,モジュールはJavaScript固有の概念ではなく,他の場所でも明確に定義されていない.もともと難しいことはみんな違うからもっと難しいと思う
MDN対モジュールは以下のように定義される.
関数とその宣言時の語彙環境の相互関係による現象.
見ても意味が分からないので省略して、簡単な例を挙げてCloserの状況を説明します.
例01
var outer = function(){
	var a = 1;
  	var inner = function(){
  		return ++a;
  	}
    return inner;
}
var outer2 = outer();
console.log(outer2()); // 2
console.log(outer2()); // 3
上のコードのouter関数でaを宣言し、inner関数でaを返し、最終的にinner関数自体を返します.
では、outer関数の実行コンテキストが終了すると、outer 2変数はouterの実行結果inner関数を参照します.
内部関数の実行コンテキストの環境レコードには収集する情報がありません.outher-EnvironmentReferenceでは、内部関数が宣言する位置のLexicalEnvironmentがコピーされます.内層関数はouter関数の内部で宣言されるため、outer関数のLexicalEnvironmentが含まれます.
現在、スキャンメカニズムによれば、外部から宣言された変数aに近づき、1を増やし、outher 2を呼び出し、同様に2から1に増加する.
outerはすでに実行済みですが、outer 2関数はinnerを返す可能性があり、innerはouterのaという変数を参照しているので、outer 2を実行した瞬間にjavascriptがouterの変数aを消さないようにします.
すなわち、ある関数A(outer)で宣言された変数aを参照する内部関数B(inner)を外部に渡すと、Aの実行コンテキストが終了しても変数aは消えない.
では、こんなに長くて分かりにくいものがどこに使われているのか見てみましょう.

エンクロージャ使用例


コールバック関数で外部データを使用する場合

var fruits = ['apple', 'banana', 'peach'];
var $ul = document.createElement('ul');

var alertFruit = function(fruit){
	alert('your choice is' + fruit);
}

fruits.forEach(function(fruit){
	var $li = document.createElement('li');
  	$li.innerText = fruit;
  	$li.addEventListener('click', alertFruit.bind(null, fruit));
  	$ul.appendChild($li);
});

document.body.appendChild($ul);
alertFruit(fruits[1]);
8行目のforEach文に渡されるコールバック関数(外部)には外部変数が内部的に使用されていないため、モジュールはありません.
addEventListenerに渡されるコールバック関数(内部)では、フルーツという外部変数が使用されるため、エンクロージャが生成される.
外部コールバック関数は、果物の数に応じて実行され、毎回新しい実行コンテキストがアクティブになります.
外部コールバック関数の実行を終了するかどうかにかかわらず、イベントをクリックして各コンテキストの内部コールバック関数を実行すると、外部環境は外部コールバック関数のメキシコ環境を参照します.

アクセス権制御(情報の非表示)


情報隠蔽はプログラミング言語の重要な概念であり、あるモジュールの内部論理を最小限に抑え、モジュール間の結合度を低減し、柔軟性を高めることを望んでいる.
通常、アクセス権はpublic、private、protectedの3種類ありますが、JavaScriptではこの機能はありません.
しかし,閉パケットを用いると,関数の観点から共通と私有を区別できる.
var outer = function(){
	var a = 1;
  	var inner = function(){
    	return ++a
    }
    return inner;
}
var outer2 = outer();
console.log(outer2());
console.log(outer2());
上記コードでouter関数を終了したときに内関数を返すことにより、外部でもouter関数の領域変数aの値を読み取ることができる.回帰を通じて.outer関数を実行できますが、outer関数の内部に干渉することはできません.外部関数が返すaのみがアクセスできます.これがCloser閉鎖性を利用したprivate値です.

ローカル適用関数


局所適用関数は、m個のパラメータをn個のパラメータを受け入れる関数に渡してから、残りのパラメータを元の関数の実行結果を受け入れる関数に渡す関数です.
通常、実際の操作で使用されるdebonse関数を見てみましょう.
var debounce = function(eventName, func, wait){
	var timeoutId = null;
    return function(event){
        var self = this;
        console.log(eventName, 'event 발생')
        clearTimeout(timeoutId);
        timeoutId = setTimeout(func.bind(self, event), wait);
    }
}

var moveHandler = function(e){
    console.log('move event 처리');
}
var wheelHandler = function (e) {
    console.log('wheel event 처리');
}
document.body.addEventListener('mousemove', debounce('move', moveHandler, 500));
document.body.addEventListener('mousewheel', debounce('wheel', wheelHandler, 700));
最初のイベントが発生した場合、7行目はtimeoutのキューにwait時間の後にfuncを実行するように要求しますが、wait時間の前に同じイベントが発生した場合、6行目の前に格納されたキューを初期化し、7行目に新しいキューを登録します.
最終的に、各イベントが前のイベントのwait時間内に発生する限り、最後のイベントのみが初期化されて実行されません.

整理する

  • エンクロージャとは、ある関数を参照して宣言した変数の内部関数を外部に渡すと、関数の実行コンテキストが終了してもその変数は消えない現象を指す.
  • 外部から
  • 内部関数を伝達する典型的な方法は、returnおよびコールバック関数を使用することである.