関数に関するトピックの最後として、クロージャーについて触れておこう。簡単にいうと、クロージャーとは、関数が定義された環境にある変数を利用できる機能と考えられる。JavaScriptでは、関数を入れ子にできるので、「関数Aの中で関数Bが定義されているときに、関数Bでは関数Aで使われている変数も使える」ということになる。なお、クロージャーはTypeScript独自の機能ではなく、JavaScriptに備わっている機能である。ここでは簡単な例を使って書き方と仕組みを見ていこう。以下のコードは、関数を呼び出すたびに引数で指定した値を加算していく例である。
function getSerialNumber() {
var origin = 0; // (1)
function countUp(delta: number): number { // (2)
return origin += delta;
}
return countUp; // (3)
};
var inside = getSerialNumber(); // (4)
alert(inside(2)); // (5)
alert(inside(3)); // (6)
window.close();
プログラムを実行すると、(5)でgetSerialNumber関数内で定義されているローカル変数origin(初期値は0)に2が加算されるので、「2」という結果が表示され、続いて(6)でローカル変数originに3が加算されるので、「5」という結果が表示される。
では、コードを見ていく。関数の構造が複雑なので、図を使って解説しよう。変数originはgetSerialNumber関数のスコープで宣言されていること、getSerialNumber関数は、countUp関数の参照を返すことに注意して読み解いてほしい。図内の丸数字はコードのコメントに付けた数字と対応している。
(4)ではcountUp関数の参照が返され変数insideに代入される。これで、変数insideを使ってcountUp関数が実行できるようになる。
(5)では変数insideで参照されるcountUp関数が実行されるので、originの値が2になる。getSerialNumber関数で宣言されている変数originが使えることに注目しよう。
(6)でも同様にcountUp関数が実行される。getSerialNumber関数の実行はすでに終了しているが、countUp関数への参照はまだ生きている(変数insideに代入されている)ので、originの値が使える。従って、originの値に3が加算され、5になる。
なお、上のコードをよく見ると、countUpという関数名は必ずしも必要でないことが分かる。ということは、countUp関数を無名関数にしてもいいということだ。さらにinsideという変数を使わずに書くこともできる。以下がその例である。
var countUp = function () { // (1)
var origin = 0;
return function (delta: number) { // (3)
return origin += delta;
};
} (); // (2)
alert(countUp(2));
alert(countUp(3));
window.close();
(1)と(2)を見ると、getSerialNumber関数を無名関数にして、最後に()を付けていることが分かる。このように、関数の定義の後に()を付けると即時実行関数となり、定義と同時に実行される。実行結果として返されるのは、内側にある(3)の関数への参照である。これが変数countUpに代入されるので、変数countUpを使って(3)の関数が呼び出せるようになる。
なお、アロー関数式を使うとさらに簡潔に書けるので、以下に示しておこう。
var countUp = (() => {
var origin = 0;
return (delta: number) => {
return origin += delta;
};
})();
alert(countUp(2));
alert(countUp(3));
window.close();
クロージャーは、イベントハンドラーがそのコンテキスト(環境)に関するデータにアクセスするときなど、さまざまな場面で使われているが、なかなか理解しづらい考え方である。アロー関数式を使った簡潔な書き方から考えると混乱するかもしれないが、ここで見たように、冗長な書き方から少しずつ簡潔に書き直しながら理解すれば納得できるだろう。
今回はTypeScriptの関数についてのトピックをいくつか取り上げた。初心者にとって、なかなか越えがたい壁であるジェネリックスやクロージャーなどの考え方について、少しでも理解を深めてもらえたのではないかと思う。次回はJavaScriptにはない、TypeScriptの便利な機能であるクラスについて、基礎的な事柄を解説する。
Copyright© Digital Advantage Corp. All Rights Reserved.