JavaScriptのクロージャの仕組み

JavaScript にはクロージャという仕組みがあります。ここでは、JavaScript における基本的な仕組みについて説明します。

クロージャとは

ある関数が定義された際の「環境」を保持しているとき、その関数をクロージャと呼びます。具体的に説明すると、ある関数がスコープチェーンによって外側にある変数への参照を保持しているとき、その関数はクロージャといえます。

よく見られるカウンタ関数のクロージャの例を見てみましょう。

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 の値はインクリメントされていきます。

クロージャの利点

ここではクロージャの基本的な仕組みのみ説明しましたが、クロージャを使うことの主な利点を簡単に列挙します。

・グローバル変数の使用を回避することで、保守性を高めることができる。

・いわゆるオブジェクト指向プログラミングにおけるカプセル化、隠蔽を行える。