私はJavaScriptの本を何冊か読んでいますが、クロージャと副作用について常に耳にします。どういうわけか、私は彼らが実際に何であるかを理解することができません。誰かが私に彼らが平易な英語と例で何であるかを説明できますか?(グラフィックデザイナーのプログラミングレベルの人に説明していたように)。
6 に答える
副作用はより簡単な概念です。「純粋関数」は、その入力値を出力値にマップする関数ですfunction plus(x, y) { return x + y; }
。「副作用」とは、その戻り値以外の効果のことです。したがって、たとえば:
function plusWithSideEffects(x, y) {
alert('This is a side effect');
return x + y;
}
警告ダイアログを表示する(そしてユーザーの操作を必要とする)という副作用があります。すべてのコード関数にはいくつかの副作用があります(それらはすべてメモリを消費し、他に何もないとしても時間がかかります)が、人々が副作用について話すとき、彼らは多くの場合、IO(上記のアラートダイアログのように)または状態の書き込みのいずれかに最も関心があります関数の実行期間を超えて存続します。
副作用の課題は、関数を推論して再利用するのが難しくなることです。(「純粋関数」にできるだけ近い関数は、「1つのことをうまく行う」傾向があるため、推論して再利用する方がはるかに簡単です。)
副作用のある関数は、値を返す以外のことを行います(ただし、それを行う場合もあります)。特定の引数に対するすべての関数呼び出しをそれらの引数の値に置き換えることができ、プログラムの動作が同じである場合、副作用はありません。これには、関数が指定された引数に対して常に同じ値を返す必要があります。
つまり、と仮定しますf(1,2) == 12
。いつでも置き換えることができ、プログラムが同じように動作する場合f(1,2)
、12
これらf
の引数に対する副作用はありません。一方、ある場所f(1,2) == 12
と別の場所にあるf(1,2) == 13
場合は、f
副作用があります。同様に、プログラムがf(1,2)
12に置き換えた後に電子メールの送信を停止した場合、f
副作用があります。一般に、f(x,y) == z
(zはxとyに依存します)、すべてのf(x,y)
呼び出しをいつでもで置き換えることができる場合z
、f
副作用はありません。
副作用のあるいくつかの単純な関数:
// doesn't always return the same value
function counter() {
// globals are bad
return ++x;
}
// omitting calls to `say` change logging behavior
function say(x) {
console.log(x);
return x;
}
副作用:
副作用は、2つのことを同時に行うものと考えてください。例えば:
副作用の典型的な例:
var i = 1;
var j = i++;
副作用はで発生しi++
ます。ここで起こることはj
1になり、次に i
増分されて2になります。つまり、2つのことが起こり、副作用はi
2になりました。
閉鎖:
次のようなリンクのチェーンを視覚化します:<> <> <> <> <><><>。このリンクのチェーンの名前がスコープチェーンと呼ばれると想像してください。次に、これらすべてのリンクが次のようにオブジェクトを接続していると想像してください:<> object <> object <>object<>。ここで、次の点に注意してください。
(1)すべてのスコープチェーンはグローバルオブジェクトで始まります。
(2)関数が定義されると、その関数のスコープチェーンが保存されます。
(3)関数が呼び出されると、新しいオブジェクトが作成され、それがスコープチェーンに追加されます。
ここで、次の例を見てください。
function counter () { // define counter
var count = 0;
return function () { return count + 1;}; // define anonymous function
};
var count = counter(); // invoke counter
この例では、counter()
が定義されている場合、counterのスコープチェーンは次のようになります:<>グローバルオブジェクト<>。次に、counter()
が呼び出されると、スコープチェーンは次のようになります。<>グローバルオブジェクト<>カウンターオブジェクト<>。その後、カウンター内に名前のない関数(無名関数と呼ばれます)が定義され、呼び出されます。一度呼び出された無名関数のスコープチェーンは次のようになります。<>グローバルオブジェクト<>カウンターオブジェクト<>無名関数オブジェクト<>
ここにクロージャー部分が入っています。気づいたら、無名関数はそのcount
外部で定義された変数を使用しています。その理由は、無名関数がスコープチェーンで定義されているすべての変数にアクセスできるためです。これがクロージャであり、格納されているスコープチェーン内の変数への参照を伴う関数です。
ただし、上記の例では、関数が返されると、呼び出し時に作成されたオブジェクトは破棄されるため、実際には意味がありません。次に、以下を見てください。
function counter () { // define counter
var count = 0;
function f() { return count + 1;}; // define f
return f; // return f
};
var count = counter(); // invoke counter
この例では、という名前の関数を返し、f
それを変数に割り当てていますcount
。これで、変数count
はスコープチェーン全体への参照を保持し、破棄されません。言い換えると、変数countは、次のようにスコープチェーンを格納します:<>グローバルオブジェクト<>カウンターオブジェクト<>無名関数オブジェクト<>。これはクロージャの力です。スコープチェーンへの参照を保持し、次のように呼び出すことができますcount()
。
例
function outer() {
var outerVar;
var func = function() {
var innerVar
...
x = innerVar + outerVar
}
return func
}
external()が死んだとき、関数func()は生き続け、これは実用的です
私はJavaScriptを初めて使用するので、クロージャについては話そうとはしません。ただし、JavaScriptを初めて使用することで、通常のプログラミング言語(Erlang)では不可能な副作用の使用を十分に認識できます。
副作用は、JavaScriptの状態を変更する通常の方法のようです。たとえば、w3cschools.comWebサイトから次の例を取り上げます。
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Paragraph changed.";
}
</script>
ここでは、入力パラメーターや戻り値はありません。代わりに、関数のスコープがグローバルであるため、ドキュメントの内容が変更されます。たとえば、これをErlangで記述した場合、ドキュメントはパラメーターとして渡され、新しいドキュメントの状態が返されます。呼び出し側プログラムを読んでいる人は、渡されたドキュメントと変更されたドキュメントが返されるのを見るでしょう。
明示的な新しい状態を返さないように呼び出された関数を見ると、プログラマーに副作用の使用の可能性を警告する必要があります。
主な副作用は、関数の内側からの外界との相互作用です。副作用の例としては、次のようなものがあります。-API呼び出しまたはHTTPリクエスト、データの変更、画面またはコンソールへの印刷、DOMクエリ/操作。例 :
var a = 12
function addTwo(){
a = a + 2; // side-effect
}
addTwo()
クロージャ
MDNによると、
クロージャを使用すると、内部関数から外部関数のスコープにアクセスできます。JavaScriptでは、関数が作成されるたびに、関数の作成時にクロージャが作成されます。
例 :
function outer(){
var a = 12; // Declared in outer function
function addTwo(){ // closure function
a = a + 2; // acessing outer function property
console.log(a)
}
addTwo();
}
outer()