No.2 クロージャー
ガーベージコレクション
ガーベージコレクションは、クロージャーを学ぶにあたりよく出る言葉なので覚えておきましょう。
JavaScriptでは不要になったメモリを解放する仕組みが備わっています。 このプロセスは「ガーベージコレクション」と呼ばれます。
この仕組みには、不要となったメモリ領域を開放してメモリ不足を解消する目的があります。 また、不要なメモリをそのままにしておくことで発生するメモリリークを防ぐことにも役立ちます。
関数内の関数
JavaScriptでは関数内に関数を定義することができます。
1 2 3 4 5 6 7 8 | function f() { function innerfunc() { console.log("Hello World!"); } innerfunc(); } f(); // "Hello World!" |
関数fを呼び出すと、関数innerfuncが呼び出されるため、”Hello World!”と表示されます。
関数を返す関数
ここでは関数fの戻り値としてinnerfuncの関数オブジェクトを返却するように改良します。
1 2 3 4 5 6 7 8 9 | function f() { function innerfunc() { console.log("Hello World!"); } return innerfunc; } var func = f(); func(); // "Hello World!" |
関数fの戻り値がinnerfuncになり、戻り値の関数オブジェクトを実行しています。
クロージャー
今から3つの例を見せます。 まず1つ目です。
1 2 3 4 5 6 7 8 | function f() { return function() { console.log("Hello World!"); } } var func = f(); func(); // "Hello World!" |
上記では、変数funcに関数fの無名関数が戻り値として格納されます。これでfuncは関数オブジェクトとなり呼び出されるようになります。
ここで見て欲しいのは関数funcの中身です。
1 2 3 | var func = function(){ console.log("Hello World!"); } |
戻り値から関数funcはこのような形になる筈です。
関数funcを呼び出すと”Hello World!”と表示されることがよく分かります。
同様にしてあと2つの例を見てみましょう。
1 2 3 4 5 6 7 8 9 | function f() { return function() { var msg = "Hello World!" console.log(msg); } } var func = f(); func(); // "Hello World!" |
上記は2つ目です。”Hello World!”という文字列を変数に格納してから表示しています。
この場合、関数funcの中身は
1 2 3 4 | var func = function(){ var msg = "Hello World!" console.log(msg); } |
このようになります。 この場合でもやはり、関数funcを呼び出せば”Hello World!”が表示されてもなんら不思議ではありません。
最後に3つ目を見てみましょう。
1 2 3 4 5 6 7 8 9 | function f() { var msg = "Hello World!" return function() { console.log(msg); } } var func = f(); func(); // "Hello World!" |
ここでは、変数msgを無名関数の外に出しています。 関数funcの中身は
1 2 3 | var func = function(){ console.log(msg); } |
となる筈ですね。 3つともが”Hello World!”を表示するプログラムでした。めでたしめでたし・・・とはなりません。 3つ目には通常では実現できない筈のことが起こっています。
関数funcの内容を見ると変数msgの中身は記述されていませんね。 ということは変数msgを参照するために、無名関数の外にある変数msgを参照することになります。 ですが、通常ならば関数fが実行されたタイミングで、無名関数の外にある変数msgは生成され、終了時にガーベージコレクションにより削除されてしまいます。 つまり変数msgは既に削除されており、参照できない筈なのです。
ではなぜ関数funcは把握できない筈の変数msgを”Hello World!”であると理解できたのでしょうか。
この理由が今回の題名でもあるクロージャーです。
クロージャーとは関数内のローカルスコープ外にある変数にアクセスすることができる仕組みです。
今回の3つ目の例ではどのように利用されているかというと、変数msgは無名関数の外で定義されていますが、クロージャーによりローカルスコープ外であっても参照されています。 よって変数msgは無名関数の中で参照されているため、関数f終了時に破棄されずガーベージコレクションの対象外にになります。 クロージャーを利用することで、意図的にガーベージコレクションの対象から外すことができるのです。
では、最後に下記を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 | function f() { var x = 1; return function() { return x++; } } var func = f(); console.log(func()); // 1 console.log(func()); // 2 console.log(func()); // 3 |
変数xは関数fが呼び出された後、作成されます。その戻り値の無名関数で参照されているため、そのままメモリ上に存在し続けます。
そのため呼び出されるごとにカウントがアップします。
今回はここまで。 次回はJavaScriptでの疑似クラス(1)です。 どうぞお楽しみに!!