私はいくつかのJavaScriptの本を読んでいて、クロージャと副作用についていつも耳にします。どういうわけか、私は彼らが実際に何であるかを理解することができません。平易な英語と例で彼らが何であるかを誰かが私に説明できますか? (グラフィックデザイナーのプログラミングレベルの人に説明していたように)。
副作用はより簡単な概念です。 「純粋関数」は、その入力値を出力値function plus(x, y) { return x + y; }
にマップする関数です。 「副作用」とは、その戻り値以外の影響のことです。したがって、たとえば:
function plusWithSideEffects(x, y) { alert("This is a side effect"); return x + y; }
警告ダイアログを表示する(そしてユーザーの操作を必要とする)という副作用があります。すべてのコード関数にはsomeの副作用があります(それらはすべてメモリを消費し、他に何もないとしても時間がかかります)が、人々が副作用について話すとき、彼らはしばしば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になりました。
閉鎖:
次のようなリンクのチェーンを視覚化します:<> <> <> <> <> <> <>。このリンクのチェーンの名前がと呼ばれていると想像してください スコープチェーン。次に、これらすべてのリンクが接続されていると想像してください オブジェクト このように一緒に:<>オブジェクト<>オブジェクト<>オブジェクト<>。ここで、次の点に注意してください。
(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()
が呼び出されると、スコープチェーンは次のようになります。<>グローバルオブジェクト<>カウンターオブジェクト<>。その後、カウンター内に名前のない関数(無名関数と呼ばれます)が定義され、呼び出されます。呼び出された無名関数のスコープチェーンは次のようになります。<>グローバルオブジェクト<>カウンターオブジェクト<>無名関数オブジェクト<>
クロージャ部分が入ってくるのはここにあります。気づいたら、無名関数はその外部で定義された変数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
}
Outside()が死ぬと、関数func()は生き続け、これは実用的です
主な副作用は、関数の内側からの外界との相互作用です。副作用の例としては、次のようなものがあります。-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()
私はJavaScriptを初めて使用するので、クロージャについては話そうとはしません。しかし、JavaScriptを初めて使用することで、通常のプログラミング言語(Erlang)では不可能な副作用の使用を十分に認識できます。
副作用は、JavaScriptの状態を変更する通常の方法のようです。たとえば、w3cschools.comWebサイトの次の例を見てください。
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Paragraph changed.";
}
</script>
ここでは、入力パラメーターや戻り値はありません。代わりに、関数のスコープがグローバルであるため、ドキュメントの内容が変更されます。たとえば、これをErlangで記述した場合、ドキュメントはパラメーターとして渡され、新しいドキュメントの状態が返されます。呼び出し側プログラムを読んでいる人は、渡されたドキュメントと変更されたドキュメントが返されるのを見るでしょう。
明示的な新しい状態を返さないように呼び出された関数を見ると、プログラマーに副作用の使用の可能性を警告する必要があります。