JavaScript にはクロージャという仕組みがあります。ここでは、JavaScript における基本的な仕組みについて説明します。
クロージャとは
ある関数が定義された際の「環境」を保持しているとき、その関数をクロージャと呼びます。具体的に説明すると、ある関数がスコープチェーンによって外側にある変数への参照を保持しているとき、その関数はクロージャといえます。
よく見られるカウンタ関数のクロージャの例を見てみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
function f() { var n = 0; function g() { alert(n); n++; } return g; } var c = f(); // 関数 g()の返り値として c に代入される c(); // 0 変数 n への参照は保持される c(); // 1 c(); // 2 |
関数 g()(および g() が代入された c)はクロージャです。
関数 f() はローカル変数 n を初期化し、子関数として定義した g() を返します。関数 g() は、alert(n) および n++ を実行しますが、g()の中では変数 n が見つからないため、ひとつ外側の親関数 f()に変数 n がないかを探しに行きます。これをスコープチェーンと呼びます(変数が見つかるまでグローバルスコープまで辿って行き、それでも見つからない場合変数の値は undefined となります)。
このように、関数g()はスコープチェーンを辿って親関数で定義された変数(環境)を参照しているクロージャであるといえます。上記の「環境を保持する」とは、関数 g() がスコープチェーンを辿って親関数 f() 内で定義されたローカル変数 n を参照できるということです。
例では、グローバル変数である c には f() の返り値である関数 g() が代入(正確には参照の代入)されています。通常、グローバルスコープからはローカルスコープを参照することができないため、関数 c() の実行時には f()内 で定義されたローカル変数 n を参照できないように思えます。しかし、c すなわち関数 g() は自身が定義された際の環境を保持しているクロージャであるため、g()の親関数 f() で定義されたローカル変数 n をスコープチェーンを辿って参照することができます。
このため、関数 c() を実行するたびに n の値はインクリメントされていきます。
クロージャの利点
ここではクロージャの基本的な仕組みのみ説明しましたが、クロージャを使うことの主な利点を簡単に列挙します。
・グローバル変数の使用を回避することで、保守性を高めることができる。
・いわゆるオブジェクト指向プログラミングにおけるカプセル化、隠蔽を行える。