※クロージャ自体のことは(なんとなく)わかることを前提としている。
※「クロージャとは」でググるとJavaScriptが関係しているページが多いので、以降JavaScriptを例に用いる。
前提となる言葉の説明
"束縛変数"・"自由変数"
あるプログラム断片に着目したときに、その断片中に出てくる変数(仮に x
とする)について、 x
が断片中で宣言されているなら x
は 束縛変数 であり、断片中で宣言されてないなら x
は 自由変数 である1。
「あるプログラム断片に着目する」のが重要で、着目する断片によって自由変数か束縛変数かは変わってくる。例えば、以下のプログラム断片では、
function(a) {
return function(b) {
return [a, b, c, d];
}
}
a
, b
は引数として宣言されているため束縛変数であり、c
, d
は自由変数である。
一方、同プログラムの内側の関数のみに着目すると、
function(b) {
return [a, b, c, d];
}
b
のみが束縛変数で、 a
, c
, d
が自由変数である。
"閉じている"("closed")
自由変数を含まないプログラム断片は、閉じている(closed)という。
例えば、次の断片は閉じた断片である(a
は束縛変数)。
function(a) {return a;}
一方、次の断片は閉じていない(開いた)断片である(b
は自由変数)。
function(a) {return b;}
閉じていない断片は、断片内で宣言されていない変数を含むため、その断片単体では実行できない。
本題
以下のコードを例に。
function getClosure() {
const x = 123;
return function(a) {
return x + a;
};
}
// 動作例
const myClosure = getClosure();
myClosure(0); // -> 123
このとき、クロージャ myClosure
は
function(a) {
return x + a;
}
である。ところが、この関数は閉じていない(変数 x
が自由変数だから)。そのため、この関数単体では使えない。
そこで、myClosure
は、x
の値が何であるかの情報(JavaScript風に書くと {x: 123}
のようなイメージ)を保持しておく必要がある。この情報を 環境 と呼ぶ。
この環境を先ほどの関数とセットで見ることで、あたかも x
が束縛変数になり関数が閉じた状態になるように見える。
このようにあたかも閉じた(closed)状態になった「関数と環境のセット」を、「クロージャ(closure)」と呼ぶのである。
まとめ
「クロージャ(closure)」という名前の由来は、自由変数を含む関数が、環境とセットになって「閉じた(closed)」状態になったから2。
参考
[Closure (computer programming) - Wikipedia # History_and_etymology]
(https://en.wikipedia.org/wiki/Closure_(computer_programming)#History_and_etymology)
-
実用的なプログラミング言語での束縛変数・自由変数の説明、難しい。大抵のサイトでは「関数中の仮引数とローカル変数が束縛変数である」となっている気がする。 ↩
-
「クロージャとして扱われる関数とそうでない関数がある」ようなニュアンスで書いたが、(少なくとも)JavaScriptでは、関数は常にクロージャである(クロージャ - JavaScript | MDN)。 ↩