カレーについて質問したところ、クロージャーについて触れられていました。閉鎖とは何ですか?それはカレーとどのように関係していますか?
カレーについて質問したところ、クロージャーについて触れられていました。閉鎖とは何ですか?それはカレーとどのように関係していますか?
回答:
ローカル変数を宣言すると、その変数にはスコープがあります。通常、ローカル変数は、宣言したブロックまたは関数内にのみ存在します。
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails
ローカル変数にアクセスしようとすると、ほとんどの言語は現在のスコープでそれを探し、次にルートスコープに到達するまで親スコープを調べます。
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works
ブロックまたは関数の処理が完了すると、そのローカル変数は不要になり、通常はメモリから削除されます。
これが通常の動作を期待する方法です。
クロージャーは、コード実行がそのブロックの外に移動した後でも、ローカル変数を保持する永続的なスコープです。クロージャーをサポートする言語(JavaScript、Swift、Rubyなど)を使用すると、それらの変数が宣言されたブロックの実行が終了した後でも、参照を保持していれば、スコープ(その親スコープを含む)への参照を保持できます。そのブロックまたはどこかに機能します。
スコープオブジェクトとそのすべてのローカル変数は関数に関連付けられており、その関数が持続する限り持続します。
これにより、関数の移植性が得られます。関数が最初に定義されたときにスコープ内にあったすべての変数は、完全に異なるコンテキストで関数を呼び出した場合でも、後で関数を呼び出すときにまだスコープ内にあると予想できます。
ポイントを示すJavaScriptの非常に単純な例を次に示します。
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
return inner; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc();
ここでは、関数内で関数を定義しました。内部関数は、を含むすべての外部関数のローカル変数にアクセスできますa
。変数a
は内部関数のスコープ内にあります。
通常、関数が終了すると、そのすべてのローカル変数が吹き飛ばされます。ただし、内部関数を返し、それを変数に割り当てて、終了fnc
後に永続化すると、が定義されたときにスコープ内にあったすべての変数も永続化されます。変数が閉じられました-クロージャ内にあります。outer
inner
a
変数a
は完全にプライベートであることに注意してくださいfnc
。これは、JavaScriptなどの関数型プログラミング言語でプライベート変数を作成する方法です。
ご想像のとおり、私がfnc()
それを呼び出すとa
、「1」であるの値が出力されます。
クロージャーのない言語では、変数a
はガベージコレクションされ、関数がouter
終了すると破棄されます。fncを呼び出すとエラーがスローされます。a
存在しない。
JavaScriptでは、変数a
スコープは関数が最初に宣言されたときに作成され、関数が存在し続ける限り存続するため、変数は存続します。
a
のスコープに属していますouter
。のスコープにinner
は、のスコープへの親ポインタがありますouter
。fnc
はを指す変数ですinner
。a
持続する限りfnc
持続します。a
閉鎖内にあります。
(JavaScriptで)例を示します。
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...
この関数makeCounterが行うことは、xと呼ばれる関数が返されることで、呼び出されるたびに1ずつカウントアップします。xにパラメーターを提供していないので、何とかカウントを覚えておく必要があります。それは字句スコープと呼ばれるものに基づいてそれを見つける場所を知っています-それは値を見つけるためにそれが定義されている場所を探す必要があります。この「隠された」値は、いわゆるクロージャーです。
これが私のカレーの例です。
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7
ご覧のとおり、パラメーターa(3)を指定してaddを呼び出すと、その値は、add3として定義している返された関数のクロージャーに含まれています。このようにして、add3を呼び出すと、加算を実行するa値の場所がわかります。
カイルの答えはかなり良いです。追加の説明は、クロージャーは基本的にラムダ関数が作成された時点でのスタックのスナップショットであることだけです。その後、関数が再実行されると、スタックは関数を実行する前の状態に復元されます。したがって、Kyleが言及しているcount
ように、ラムダ関数が実行されると、その隠された値()が使用可能になります。
まず第一に、ここにいるほとんどの人々があなたに言うこととは反対に、閉鎖は機能ではありません!だから何であるそれは?
これは、関数の「周囲のコンテキスト」(その環境と呼ばれます)で定義された一連のシンボルであり、これをCLOSED式(つまり、すべてのシンボルが定義され、値を持つため、評価できる式)にします。
たとえば、JavaScript関数がある場合:
function closed(x) {
return x + 3;
}
そこに出現するすべてのシンボルが定義されているため(それらの意味は明確です)、これは閉じた式であり、評価できます。つまり、自己完結型です。
しかし、次のような関数がある場合:
function open(x) {
return x*y + 3;
}
定義されていないシンボルが含まれているため、オープンな式です。すなわち、y
。この関数を見ると、y
その意味と意味がわかりません。その値がわからないため、この式を評価できません。つまり、何y
を意味するのかがわかるまで、この関数を呼び出すことはできません。これy
は自由変数と呼ばれます。
これy
は定義を要求しますが、この定義は関数の一部ではありません。「周囲のコンテキスト」(「環境」とも呼ばれます)のどこかで定義されています。少なくともそれが私たちが望むことです:P
たとえば、グローバルに定義できます。
var y = 7;
function open(x) {
return x*y + 3;
}
または、それをラップする関数で定義することもできます。
var global = 2;
function wrapper(y) {
var w = "unused";
return function(x) {
return x*y + 3;
}
}
式の中の自由変数にそれらの意味を与える環境の部分は、クロージャです。それはこのように呼ばれます、なぜならそれは自由な変数のすべてにこれらの欠けている定義を提供することによって開いた式を閉じたものに変えるからそれを評価できるからです。
上記の例では、内部関数(必要がないため名前を付けていません)はオープンな式ですy
。これは、その中の変数が自由であるためです。その定義は、関数の外側、それをラップする関数内にあります。 。その無名関数の環境は、変数のセットです。
{
global: 2,
w: "unused",
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
ここで、クロージャはこの環境の一部であり、すべての自由変数の定義を提供することによって内部関数を閉じます。私たちの場合、内部関数の唯一の自由変数はy
だったので、その関数のクロージャはその環境のこのサブセットです:
{
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
環境で定義された他の二つのシンボルがありませんの一部閉鎖、それは実行するためにそれらを必要としないため、その関数の。彼らはそれを閉じる必要はありません。
その背後にある理論の詳細はこちら:https : //stackoverflow.com/a/36878651/434562
上記の例では、ラッパー関数がその内部関数を値として返すことに注意してください。この関数を呼び出す瞬間は、関数が定義(または作成)された瞬間から離れていてもかまいません。特に、そのラッピング関数は実行されておらず、呼び出しスタックにあったパラメーターはもうありません:Pこれは問題y
です。これは、呼び出されたときに内部関数がそこにある必要があるためです。言い換えると、ラッパー関数を何とか存続させ、必要なときにそこに存在するためには、クロージャーからの変数が必要です。したがって、内部関数はスナップショットを作成する必要がありますこれらの変数のを作成して、クロージャーを作成し、後で使用するために安全な場所に保存する必要があります。(呼び出しスタックの外のどこか。)
そして、このため、クロージャーという用語を、使用する外部変数のスナップショットを実行できる特別なタイプの関数、またはこれらの変数を後で使用するために格納するために使用されるデータ構造と混同することがよくあります。しかし、これらがクロージャ自体ではないことを理解していただければ幸いです。これらは、プログラミング言語でクロージャを実装する方法、または必要に応じて関数のクロージャからの変数が存在できるようにする言語メカニズムです。クロージャーに関しては多くの誤解があり、(不必要に)この主題を実際よりもはるかに混乱させ、複雑にします。
クロージャの理解を容易にするために、手続き型言語でクロージャがどのように実装されるかを調べることが役立つ場合があります。この説明は、Schemeでのクロージャーの単純化された実装に従います。
まず、名前空間の概念を紹介する必要があります。コマンドをSchemeインタープリターに入力すると、式内のさまざまなシンボルを評価してその値を取得する必要があります。例:
(define x 3)
(define y 4)
(+ x y) returns 7
定義式は、xのスポットに値3を、yのスポットに値4を格納します。次に(+ xy)を呼び出すと、インタープリターは名前空間の値を検索し、操作を実行して7を返すことができます。
ただし、Schemeには、シンボルの値を一時的に上書きできる式があります。次に例を示します。
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
letキーワードが行うことは、xを値5とする新しい名前空間を導入することです。yが4であることは引き続き確認でき、合計は9に戻ります。また、式が終了するとxこの意味では、xはローカル値によって一時的にマスクされています。
手続き型言語とオブジェクト指向言語には同様の概念があります。関数内でグローバル変数と同じ名前の変数を宣言すると、同じ効果が得られます。
これをどのように実装しますか?簡単な方法は、リンクリストを使用することです。ヘッドには新しい値が含まれ、テールには古い名前空間が含まれます。シンボルを調べる必要があるときは、頭から始めて尾まで進みます。
とりあえず、ファーストクラスの関数の実装にスキップしましょう。関数は、多かれ少なかれ、関数が呼び出されたときに実行される一連の命令であり、戻り値が最大になります。関数を読み込むとき、これらの命令をバックグラウンドで保存し、関数が呼び出されたときに実行できます。
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns ?
xを3と定義し、plus-xをそのパラメーターyにxの値をプラスしたものと定義します。最後に、xが新しいxでマスクされている環境でplus-xを呼び出します。この値は5です。関数plus-xの演算(+ xy)を格納するだけの場合は、コンテキスト内にあるためxが5の場合、返される結果は9になります。これは、動的スコープと呼ばれるものです。
ただし、Scheme、Common Lisp、および他の多くの言語には、字句スコープと呼ばれるものがあります。操作(+ xy)の格納に加えて、その特定の時点で名前空間も格納します。このようにして、値を調べると、このコンテキストではxが本当に3であることがわかります。これはクロージャです。
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
要約すると、リンクされたリストを使用して、関数定義時の名前空間の状態を格納できます。これにより、囲んでいるスコープから変数にアクセスでき、残りの変数に影響を与えずに変数をローカルでマスクできます。プログラム。
Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
クロージャーがお尻を蹴る理由の実例は次のとおりです...これは私のJavaScriptコードから直接抜粋したものです。説明させてください。
Function.prototype.delay = function(ms /*[, arg...]*/) {
var fn = this,
args = Array.prototype.slice.call(arguments, 1);
return window.setTimeout(function() {
return fn.apply(fn, args);
}, ms);
};
そして、これはあなたがそれをどのように使うかです:
var startPlayback = function(track) {
Player.play(track);
};
startPlayback(someTrack);
たとえば、このコードスニペットが実行されてから5秒後など、再生を遅延して開始したいとします。まあそれは簡単でdelay
、それは閉鎖です:
startPlayback.delay(5000, someTrack);
// Keep going, do other things
msで呼び出すdelay
と5000
、最初のスニペットが実行され、渡された引数がクロージャーに格納されます。その後5秒後、setTimeout
コールバックが発生しても、クロージャーはそれらの変数を維持しているため、元のパラメーターを使用して元の関数を呼び出すことができます。
これはカレーの一種、または機能の装飾です。
クロージャーがないと、関数の外部でこれらの変数の状態を何らかの形で維持する必要があるため、論理的にその内部に属しているもので関数の外部のコードを散らかす必要があります。クロージャーを使用すると、コードの品質と可読性が大幅に向上します。
var pure = function pure(x){
return x
// only own environment is used
}
var foo = "bar"
var closure = function closure(){
return foo
// foo is a free variable from the outer environment
}
src:https : //leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure
クロージャは関数であり、そのスコープは変数に割り当てられた(または変数として使用された)です。したがって、名前のクロージャ:スコープと関数は、他のエンティティと同じように囲まれて使用されます。
ウィキペディアによると、閉鎖は次のとおりです。
第一級の機能を備えた言語でレキシカルスコープの名前バインディングを実装するためのテクニック。
どういう意味ですか?いくつかの定義を見てみましょう。
この例を使用して、クロージャーとその他の関連定義を説明します。
function startAt(x) {
return function (y) {
return x + y;
}
}
var closure1 = startAt(1);
var closure2 = startAt(5);
console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)
基本的には、他のエンティティと同じように関数を使用できることを意味します。それらを変更したり、引数として渡したり、関数から返したり、変数に割り当てたりすることができます。技術的に言えば、彼らは一流の市民ですであり、そのため、名前は一流の機能です。
上記の例でstartAt
は、とに割り当てられる(無名)関数を返します。ご覧のとおり、JavaScriptは他のエンティティ(ファーストクラスの市民)と同じように関数を扱います。closure1
closure2
名前バインディングとは、変数(識別子)が参照するデータを見つけることです。バインディングがどのように解決されるかを決定するものなので、ここではスコープが本当に重要です。
上記の例では:
y
は、にバインドされ3
ます。startAt
範囲、x
に結合している1
、または5
(閉鎖に応じて)。無名関数のスコープ内でx
は、値にバインドされていないため、上位(startAt
)のスコープで解決する必要があります。
ウィキペディアは言う、スコープ:
バインディングが有効であるコンピュータープログラムの領域です。名前はエンティティを参照するために使用できます。
2つの手法があります。
詳細については、この質問を確認し、ウィキペディアをご覧ください。
上記の例では、JavaScriptがレキシカルにスコープされていることがわかります。これは、x
解決されると、バインディングがstartAt
ソースコード(xを検索する無名関数が内部で定義されているstartAt
)に基づいて上部('s)スコープで検索されるためです。呼び出しスタック、関数が呼び出された方法(スコープ)に基づいていません。
私たちが呼ぶとき、私たちの例では、startAt
それはに割り当てられます(ファーストクラス)関数を返しますclosure1
とclosure2
変数を渡すので、これクロージャが作成される1
と、5
内部に保存されますstartAt
返さに同封されている、の範囲を匿名関数。私たちは経由で、この無名関数を呼び出すときclosure1
とclosure2
同じ引数(と3
)の値がy
(それはその関数のパラメータであると)すぐに見つかりますが、x
解像度がで続くので、匿名関数のスコープにバインドされていません(クロージャに保存された)(字句)上部関数スコープx
のいずれかに結合することが見出されています1
またはに5
。これで合計のすべてがわかったので、結果を返して印刷することができます。
次に、クロージャーと、クロージャーの動作を理解する必要があります。これは、JavaScriptの基本的な部分です。
ああ、そしてカリーについても学びました:複数のパラメーターを持つ1つの関数を使用する代わりに、関数(クロージャー)を使用して操作の各引数を渡します。
クロージャはJavaScriptの機能であり、関数は独自のスコープ変数へのアクセス、外部関数変数へのアクセス、およびグローバル変数へのアクセスを持ちます。
クロージャーは、外部関数が戻った後でも、その外部関数スコープにアクセスできます。つまり、クロージャーは、関数が終了した後でも、その外部関数の変数と引数を記憶してアクセスできます。
内部関数は、自身のスコープ、外部関数のスコープ、およびグローバルスコープで定義された変数にアクセスできます。また、外部関数は、独自のスコープとグローバルスコープで定義された変数にアクセスできます。
閉鎖の例:
var globalValue = 5;
function functOuter() {
var outerFunctionValue = 10;
//Inner function has access to the outer function value
//and the global variables
function functInner() {
var innerFunctionValue = 5;
alert(globalValue + outerFunctionValue + innerFunctionValue);
}
functInner();
}
functOuter();
出力は、その内部関数自身の変数、外部関数変数、およびグローバル変数値の合計である20になります。
通常の状況では、変数はスコープ規則によってバインドされます。ローカル変数は、定義された関数内でのみ機能します。クロージャーは、便宜上、このルールを一時的に破る方法です。
def n_times(a_thing)
return lambda{|n| a_thing * n}
end
上記のコードでlambda(|n| a_thing * n}
はa_thing
、ラムダ(匿名関数の作成者)によって参照されるため、クロージャーです。
次に、結果の無名関数を関数変数に入れます。
foo = n_times(4)
fooは通常のスコープ規則を破り、内部で4の使用を開始します。
foo.call(3)
12を返します。
•クロージャーはサブプログラムであり、それが定義された参照環境です。
–サブプログラムをプログラム内の任意の場所から呼び出すことができる場合、参照環境が必要です。
–ネストされたサブプログラムを許可しない静的スコープの言語は、クロージャーを必要としません
–クロージャーは、サブプログラムがネストされたスコープ内の変数にアクセスでき、どこからでも呼び出すことができる場合にのみ必要です。
–クロージャーをサポートするために、実装は一部の変数に無制限の範囲を提供する必要がある場合があります(サブプログラムが、通常は生存していない非ローカル変数にアクセスする可能性があるため)。
例
function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);
これは別の実際の例であり、ゲームで人気のあるスクリプト言語であるLuaを使用しています。stdinが使用できないという問題を回避するために、ライブラリ関数の動作を少し変更する必要がありました。
local old_dofile = dofile
function dofile( filename )
if filename == nil then
error( 'Can not use default of stdin.' )
end
old_dofile( filename )
end
old_dofileの値は、このコードブロックがそのスコープを終了すると(ローカルであるため)消えますが、値はクロージャーで囲まれているため、新しい再定義されたdofile関数はそれにアクセスできます。 「upvalue」。
Lua.orgから:
関数が別の関数で囲まれて記述されている場合、その関数は、囲んでいる関数からローカル変数に完全にアクセスできます。この機能は字句スコープと呼ばれます。それは明白に聞こえるかもしれませんが、そうではありません。字句スコープとファーストクラスの関数はプログラミング言語の強力な概念ですが、その概念をサポートする言語はほとんどありません。
クロージャー別の関数の内部で関数が定義されている場合は常に、内部関数は外部関数で宣言された変数にアクセスできます。クロージャは、例で最もよく説明されます。リスト2-18では、内側の関数が外側のスコープから変数(variableInOuterFunction)にアクセスできることがわかります。外側の関数の変数が内側の関数によって閉じられている(またはバインドされている)。したがって、用語の閉鎖。コンセプト自体は非常にシンプルで、かなり直感的です。
Listing 2-18:
function outerFunction(arg) {
var variableInOuterFunction = arg;
function bar() {
console.log(variableInOuterFunction); // Access a variable from the outer scope
}
// Call the local function to demonstrate that it has access to arg
bar();
}
outerFunction('hello closure!'); // logs hello closure!
ソース:http : //index-of.es/Varios/Basarat%20Ali%20Syed%20(auth .) -Beginning%20Node.js-Apress%20(2014).pdf
閉鎖をより深く理解するには、以下のコードをご覧ください。
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
ここでは何が出力されますか?0,1,2,3,4
それは5,5,5,5,5
閉鎖のためではありません
では、どのように解決するのでしょうか?答えは以下の通りです:
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
簡単に説明します。関数が最初のコードで5回呼び出されてforループを呼び出すまで何も起こらなかった場合、すぐには呼び出されませんでした。つまり、1秒後に呼び出された場合、これは非同期なので、forループが終了して値5を格納します。 var iで、最後にsetTimeout
関数を5回実行して印刷します5,5,5,5,5
ここでは、IIFE、つまり即時呼び出し関数式を使用してどのように解決するかを示します。
(function(j){ //i is passed here
setTimeout(function(){
console.log(j);
},1000);
})(i); //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4
詳しくは、実行コンテキストを理解してクロージャを理解してください。
let(ES6機能)を使用してこれを解決するもう1つの解決策がありますが、上記の機能の下で機能します
for(let i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
Output: 0,1,2,3,4
=>詳細な説明:
メモリ内で、forループを実行すると、以下のようになります。
ループ1)
setTimeout(function(){
console.log(i);
},1000);
ループ2)
setTimeout(function(){
console.log(i);
},1000);
ループ3)
setTimeout(function(){
console.log(i);
},1000);
ループ4)
setTimeout(function(){
console.log(i);
},1000);
ループ5)
setTimeout(function(){
console.log(i);
},1000);
ここで私は実行されず、完全なループの後、var iはメモリに値5を格納しましたが、そのスコープはその子関数で常に表示されているため、関数がsetTimeout
5回裏返しに実行されると、印刷されます5,5,5,5,5
これを解決するには、上記のIIFEを使用してください。
カリー化:引数のサブセットを渡すだけで、関数を部分的に評価できます。このことを考慮:
function multiply (x, y) {
return x * y;
}
const double = multiply.bind(null, 2);
const eight = double(4);
eight == 8;
クロージャー:クロージャーは、関数のスコープ外の変数にアクセスすることに他なりません。関数内の関数またはネストされた関数はクロージャーではないことを覚えておくことが重要です。クロージャーは、関数スコープ外の変数にアクセスする必要がある場合に常に使用されます。
function apple(x){
function google(y,z) {
console.log(x*y);
}
google(7,2);
}
apple(3);
// the answer here will be 21
閉鎖は非常に簡単です。次のように考えることができます:クロージャ=関数+その語彙環境
次の関数を考えてみましょう:
function init() {
var name = “Mozilla”;
}
上記の場合の閉鎖はどうなりますか?関数init()とそのレキシカル環境の変数、つまり名前。 クロージャ = init()+名前
別の関数を考えてみましょう:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
ここの閉鎖は何ですか?内部関数は外部関数の変数にアクセスできます。displayName()は、親関数init()で宣言された変数名にアクセスできます。ただし、displayName()の同じローカル変数が存在する場合は、それらが使用されます。
クロージャ1: init関数+(名前変数+ displayName()関数)->字句スコープ
クロージャ2: displayName関数+(名前変数)->字句スコープ
プログラミングの状態は、単に物事を覚えていることを意味します。
例
var a = 0;
a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3
上記の場合、状態は変数 "a"に格納されます。続いて、「a」に1を数回追加します。私たちができるのは、その値を「記憶」できるからです。状態保持者 "a"はその値をメモリに保持します。
多くの場合、プログラミング言語では、物事を追跡し、情報を覚えておき、後でアクセスしたい場合があります。
これは、他の言語で、一般的なクラスを使用することにより達成されます。クラスは、変数と同様に、その状態を追跡します。そして、そのクラスのインスタンスもまた、その中に状態を持っています。状態とは、単に保存して後で取得できる情報を意味します。
例
class Bread {
constructor (weight) {
this.weight = weight;
}
render () {
return `My weight is ${this.weight}!`;
}
}
「レンダー」メソッド内から「ウェイト」にアクセスするにはどうすればよいですか?まあ、状態のおかげで。クラスBreadの各インスタンスは、その情報を格納できるメモリ内の場所である「状態」からそれを読み取ることにより、独自の重みをレンダリングできます。
今、JavaScriptは非常にユニークな言語ですあり、これまではクラスがありませんでした(現在はありますが、内部では関数と変数しかありません)。クロージャーは、JavaScriptが物事を記憶して後でアクセスする方法を提供します。
例
var n = 0;
var count = function () {
n = n + 1;
return n;
};
count(); // # 1
count(); // # 2
count(); // # 3
上記の例では、変数を使用して「状態を維持する」という目標を達成しました。これは素晴らしい!ただし、これには変数(「状態」ホルダー)が公開されるという欠点があります。私たちはもっとうまくやることができます。クロージャーを使用できます。
例
var countGenerator = function () {
var n = 0;
var count = function () {
n = n + 1;
return n;
};
return count;
};
var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
これで「カウント」関数がカウントできるようになりました。状態を「保持」できるため、そうすることしかできません。この場合の状態は変数「n」です。この変数は現在閉じています。時間と空間は閉鎖されています。そのうちに、それを回復したり、変更したり、値を割り当てたり、直接操作したりすることができないためです。「countGenerator」関数内で地理的にネストされているため、空間内。
なぜこれが素晴らしいのですか?他の洗練された複雑なツール(クラス、メソッド、インスタンスなど)を使用しなくても、1。隠蔽2.遠くからの制御
状態、つまり変数 "n"を隠し、プライベート変数にします!また、この変数を事前定義された方法で制御できるAPIも作成しました。特に、「count()」のようにAPIを呼び出すと、「距離」から「n」に1が追加されます。形や形は、API以外では誰も「n」にアクセスできません。
クロージャーは、これがなぜかという大きな理由です。