javascriptのクロージャ

メモ化された fibonacci.js

var fibonacci = function () {
    var memo = [0, 1];
    var fib = function (n) {
      var result = memo[n];
      if (typeof result !== 'number') {
        result = fib(n -1) + fib(n -2);
        memo[n] = result;
      };
      return result;
    };
    return fib;
}();
for (var i = 0; i <=100; i +=1) {
    document.writeln('// ' + i + ': ' + fibonacci(i) +'<br>');
}

入れ子状になった関数の戻り値を、配列やオブジェクトに記録(メモ化)すると、再帰的な関数の呼び出し回数を大幅に減らすことができます。メモ化についてはクロージャを使ってメモ化 – maru source  で、フィボナッチ数列を例に詳しく解説されていますので割愛して、今回は関数のクロージャについていまいち良く分からなかったので調べてみました。

8.6 クロージャ

最近の多くのプログラム言語と同じように、JavaScript では構文スコープを使います。つまり、関数を実行するときには、関数が定義されたときに有効であった変数スコープを使います。関数が呼び出されたときに有効な変数スコープではありません。(中略) 関数オブジェクトと、関数の変数の名前解決に使われるスコープを組み合わせたものを、コンピュータサイエンスの分野ではクロージャと呼んでいます。
正確に言えば、JavaScript の関数はすべてクロージャです。

ここまでは何を言っているのか良く解りませんね。実際にコードを見てみましょう。
クロージャを理解するためには、まず入れ子型関数の構文スコープについて考えてみましょう。

var scope = "global scope";        // グローバル変数。
function checkscope() {
    var scope = "local scope";        // ローカル変数。
    function f() { return scope; }  // ここでのスコープ中の値が戻される。
    return f();
}
checkscope()                        // => "local scope"

checkscope()関数は、ローカル変数を宣言し、その後、この変数の値を返す関数を定義し呼び出されます。checkscope() を呼び出したときに、”local scope” が戻されることに疑問を抱かないと思います。ここで、このコードを少し変更してみましょう。次のコードでは何が返されるかわかりますか。

var scope = "global scope";        // グローバル変数。
function checkscope() {
    var scope = "local scope";        // ローカル変数。
    function f() { return scope; }  // ここでのスコープ中の値が戻される。
    return f;
}
checkscope()()                        // 何が返されるか?

今回のコードでは括弧がcheckscope()から外に移動しただけです。入れ子型の関数を呼び出して戻りを返すのではなく、入れ子型の関数オブジェクト自身を返しています。括弧をコードの最後の行に動かすことで、入れ子型の関数を定義した場所の外側で呼び出した場合、どうなるでしょう。構文スコープの基本となる規則を思い出してください。JavaScript の関数は、定義されたときに有効なスコープチェーンを使って、実行される、という規則です。入れ子型の関数 f() は、変数 scope の値が “local scope” にバインド(束縛)されているスコープチェーンの下で定義されました。この変数バインドは、 f を実行するときも有効です。どこで呼び出されたとしても、これは変わりません。したがって、先のコードの最後の行が返すのは  “local scope” です。”global scope” ではありません。

先の fibonacci.js の例では入れ子になったローカル 関数、 fib = function (n) がfibonacci(i)で呼び出されて、ローカル変数が返されたというわけです。();