PR

変数を内包するクロージャを生成

 関数はオブジェクトなので、関数を返り値として返すこともできます。これにより「状態変数を持つクロージャ」を生成する関数を書くことができます。

 JavaScriptの関数は、オブジェクトであると同時にクロージャとしての性質を持つこともできます。クロージャの特徴は、自身が定義されたときの環境(変数や関数など)を覚えており、実行時の環境ではなく定義時の環境で実行されることです。言葉で説明するだけではわかりにくいと思うので、クロージャの具体的なサンプルを使って説明していきましょう(リスト11)。

リスト11●状態を表す変数を中に閉じ込めたクロージャの例
リスト11●状態を表す変数を中に閉じ込めたクロージャの例

 counter関数は、返り値として「変数cをインクリメントする関数」(1)を返します。ローカル変数は、通常は関数の終了とともに破棄されます。しかし、counter関数が返り値として返す関数は、クロージャとしての性質により、定義時の環境、すなわちcounter関数のローカル変数cを覚えています(2)。いわば、counter関数が終了しても、クロージャによりローカル変数cが生かされているのです。

 このクロージャは、呼び出されるたびに変数cをインクリメントしていきます。それを示したのが(3)の部分です。

 クロージャの特徴をもう少し見てみましょう(リスト12)。ここでは、myCount1とmyCount2という2つのクロージャを作っています。これらは、いずれもcounter関数のローカル変数cを参照します。にもかかわらず、それぞれの変数cは独立した値を持っていることがわかります。

リスト12●それぞれのクロージャが保持するローカル変数は独立している (counter関数はリスト11のものを使用)
リスト12●それぞれのクロージャが保持するローカル変数は独立している (counter関数はリスト11のものを使用)
[画像のクリックで拡大表示]

 この理由は、スコープチェーンを考えれば簡単にわかります。Callオブジェクトは関数を呼び出すたびに生成されます。つまり、myCount1を作るためにcounter関数を呼び出した時に作られるCallオブジェクトと、myCount2を作るためにcounter関数を呼び出した時に作られるCallオブジェクトは別のものです。

 したがって、これらのCallオブジェクトに含まれるローカル変数cも別のものになり、myCount1とmyCount2のスコープチェーンでは、ローカル変数cはそれぞれ別々に存在することになります。