クロージャのトピックに取り組むには、JavaScriptで可変スコープがどのように機能するかを検討する必要があります。基本的な機能と関連した問題を見てみましょう:
この関数は、その後宣言し、変数
counter
に値0を割り当て、
function uniqueInteger(){
var counter = 0;
return ++counter;
}
console.log(uniqueInteger()); // returns '1'
console.log(counter); // undefined
はインクリメントその値を返します。わかるように、関数が実行されている間は、変数counterは関数内でアクセス可能です。しかし、関数が返った後は、uniqueInteger()
関数のスコープの一部であるため、変数は定義されなくなりました。変数counter
はです。です。機能uniqueInteger()
のみがアクセスできます。 しかし、変数の値を次回に呼び出すときには、counter
は1で始まり、0ではなく始めるように、変数の値を記憶したければどうでしょうか?1つのアプローチは、関数の外で変数counter
を宣言することです。それはもはや私的でなくなり、他の関数はカウンタ変数を変更することができます。
この問題を回避するには、関数の仕組みを調べる必要があります。関数が呼び出されると(上で見たように)、その変数は返された後も存在しなくなります。しかし、その機能の範囲内で、任意のネストされた関数は、まだその親機能の「プライベート」変数にアクセスすることができるようになります:
function uniqueInteger(){
var counter = 0;
// an example of nested function accessing the counter variable:
return (function() { return ++counter })();
}
console.log(uniqueInteger()); // returns '1'
console.log(counter); // undefined
*括弧付きの呼び出しの数は()定義された関数の数に一致していることに注意してください。ネストされた関数は、自己起動され、外部関数がラインconsole.log(uniqueInteger());
したがって同じ機能で呼び出されるように書くことができます。
function uniqueInteger(){
var counter = 0;
// an example of nested function accessing the counter variable:
return function() { return ++counter };
}
console.log(uniqueInteger()()); // returns '1'
console.log(counter); // undefined
私たちが見ることができるように、ネストされた関数は、まだ変数counterにアクセスしていますuniqueInteger()
関数は "1"を返します。 uniqueInteger()
が返された後もカウンター変数は消えますが(この問題は後で解決します)、ネストされた関数がcounter
にアクセスできるという事実により、この変数に対して何かを行い、その結果を返すことができます。 uniqueInteger()
が呼び出されるたびに、同じ「スコープチェーン」が存在します。非常に単純化すると、これはレキシカルスコープと呼ばれます。
ここで、関数が復帰した後に消える可変カウンタの問題について説明しましょう。ここで起こるのは、ガベージコレクションと呼ばれるJavaScriptの機能の結果です。関数とその変数がもはや使用されなくなると、関数とその変数は "投げ捨てられ"ます。しかし、その関数の戻り値への参照がある場合(たとえば、その関数が外部変数に割り当てられている場合)、その「スコープチェーン」にしたがって、その内部の変数とともに「記憶」されます上記:
function uniqueInteger(){
var counter = 0;
return function() { return ++counter };
}
var uniqueInt = uniqueInteger();
console.log(uniqueInt()); // returns '1'
console.log(uniqueInt()); // returns '2'
console.log(counter) // still "undefined"
何が起こったのですか?ネストされた関数の戻り値を外部変数に保存したため、変数counter
はガベージコレクトされず、次回のuniqueInt()
が呼び出されたとき、counterはまだ1に等しいと言えます。 。私たちは目標を達成しました。変数を定義しないで呼び出し間の変数を記憶し、非公開にする機能を作成しました。
者らは、上記関数の定義式として、この機能を書き換えることができる:
var uniqueInteger = (function(){
var counter = 0;
return function() { return ++counter };
})();
console.log(uniqueInteger()); // returns '1'
console.log(uniqueInteger()); // returns '2'
console.log(counter) // still "undefined"
注二つの関数、したがってつの対応する呼び出しがまだ存在すること。このときだけ外部関数が自己呼び出され、関数自体でなく戻り値を変数uniqueInteger
に保存します。それ以外の場合は、変数uniqueInteger
の戻り値はfunction() { return ++counter; }
になります。上で説明した手法は、本質的には、関数内で関数(*またはオブジェクト)を使用して、呼び出しを実行して非公開にしながら内部状態を保存する内部値を操作するクロージャです。
var uniqueInteger = (function(){
var counter = 0;
return {
value: function() { return counter },
count: function() { return ++counter },
reset: function() { counter = 0; return counter;}
};
})();
console.log(uniqueInteger.value()); // 0
uniqueInteger.count();
console.log(uniqueInteger.value()); // 1
uniqueInteger.reset()
console.log(uniqueInteger.value()); // again 0
このパターンは、あなたが持つ独自のプライベート変数で動作する自己完結型の関数オブジェクトを持つことができます:あなたはまた、外部の関数の値を操作する機能を持つオブジェクトを返すことができます*
名前の衝突や悪意のある外部からの改ざんのリスクを排除します。
閉鎖も苦労しましたが、文献を読んでいれば、結局それを得るでしょう。
私は 'x'の上に2つのクロージャを持っていますが、どちらのメソッドも変数を共有していますか?私は過剰な閉鎖がパフォーマンスに悪いことを読んだ、閉鎖なしで同じことを行う方法はありますか? – vtortola