クロージャ/JavaScript

1つの関数を、複数の関数として使えると便利です。又、その関数を途中で変更出来ると、使い勝手が広がります。

本項では、クロージャの使い方について説明します。

クロージャの概要

 クロージャを使うと、1つの関数を様々な状態で利用する事が出来ます。

クロージャの概要

 上の図では、x + yを計算する関数に対して、x = 10と20の状態をクロージャ(test1とtest2)として作成し、引数yを与えて利用しています。つまり、クロージャを利用すると、1つの関数を定義する事で、複数の状態を持つ関数が使えます。

 これは、関数にxとyの引数を持たせ、その都度xに10、yに5等を渡せば同じ事が出来ますが、引数の場合はxが10という状態は関数の処理が終わると消えます。クロージャは状態を保つため、xが10の状態を保ち、後で再利用して計算等に使えます。又、その結果を利用して、xが10という状態から、xが15という状態に変える事が出来ます。

クロージャは可変

 上のtest1は、10 + yから15 + yに状態が変わっています。10 + yの状態では、引数yに5を与えると結果は15になりますが、15 + yの状態で引数yに5を与えると結果は20になります。つまり、test1を同じ引数で呼び出しても、その状態によって結果を変える事が出来ます。

クロージャの作成

 以下のスクリプトは、「関数の使い方」のスコープで説明した通り、エラーになります。

【ローカル変数が消えてエラーになる例】
<script>
function func1() {
    var x = 10;
}

function func2(y) {
    return x + y;
}

func1();
document.write(func2(5));
</script>

 赤字の関数呼び出しが終わるとxが消えてしまい、青字でfunc2を実行した時に、x + yが実行出来ないためです。

 ローカル変数は関数が終わると消えますが、クロージャでは状態として残せます。

【クロージャの状態保存例】
<script>
function func1(x) {
    function func2(y) {
        return x + y;
    }
    return func2;
}

var myValue1 = func1(10);
document.write(myValue1(5));
</script>

 赤字部分は、関数func2をfunc1の入れ子にしています。青字部分でfunc1のxに10を渡しており、この状態を維持したクロージャmyValue1が作られます。

 myValue1はxが10という状態を維持したfunc2です。このため、緑字部分のようにmyValue1(5)と引数yに5を渡すと、x + yが実行されて15が表示されます。

 クロージャは状態を覚えている関数で、1つの関数定義から複数作成出来ます。上の例で、var myValue2 = func1(20);とすると、xが20の状態でクロージャが作成されます。

クロージャの作成

 このように、func1という関数から複数のクロージャを作成する事が出来ます。

 尚、クロージャを作らずにfunc1(10)(5);を実行しても15の結果が得られます。この方法は既に説明した通り、関数を呼び出す度に、再度全ての引数を指定し直す必要があります。

状態の変更

 クロージャが保持している状態は、変更出来ます。

【クロージャの状態が変わる例】
<script>
function func1(x) {
    function func2(y) {
        return x = x + y;
    }
    return func2;
}

var myValue1 = func1(10);
document.write(myValue1(5));
document.write(myValue1(5));
</script>

 先の例と比較すると、赤字部分の式がx + yからx = x + yに変更になっています。青字部分は追加です。

 document.write(myValue1(5));と全く同じ記述が2つありますが、2つの結果は異なります。1つ目は15、2つ目は20と表示されます。1つ目のmyValue1(5)を実行した時にx = x + 5が計算され、xが15に変わるためです。

 このように、クロージャは一旦作成されても固定ではなく、必要に応じて状態を変更し、異なる結果を得る事が出来ます。

クロージャのメリット

 クロージャは、直前の状態を保持しながら、一部の状態を変化させる事が出来ます。又、1つの関数から複数のクロージャを作成出来ます。このメリットを示すため、1〜10のトランプをランダムに表示し、1枚ずつ裏返していく時のスクリプトを例に説明します。

【前の状態を保持しつつ1枚ずつトランプを裏返す例】
<script>
function func1(x) {
    var z = new Array;
    for ( var i = 1; i <= x ; i++ ) {
        z[i] = Math.floor( Math.random() * 10 ) + 1;
    }
    function func2(y) {
    z[y] = 99;
    for ( var i = 1; i <= x; i++ ) {
        document.write('<img src="image/trump/1' + z[i] + '.png" alt="トランプ" /> ');
        }
    }
    return func2;
}

var myValue1 = func1(3);
myValue1(1);
document.write("<br>");
myValue1(2);
</script>

 赤字部分は、配列zに対してxで指定された要素の数だけ、1〜10のランダムな数字を代入しています。これが、トランプの数字に対応しています。今回は、var myValue1 = func1(3);でクロージャを作成しているので、z[1]〜z[3]にランダムなトランプの数字を代入している事になります。

 青字部分は、yで指定された要素に対し、99を代入しています。これが、トランプの裏返しの画像に対応しています。緑部分は、要素を順番に取り出し、対応する数字のトランプを画像表示しています。今回の例ではx = 3なので、1回の実行で3枚のトランプが表示されます。

 上記は、以下のように表示されます。

 上段はmyValue1(1);の結果で、下段はmyValue1(2);の結果です。

 myValue1(1);は、左から1枚目を99に入れ替える処理のため、上段は左から1枚目だけ裏返しで表示され、2枚目と3枚目はランダムなトランプが表示されます。

 myValue1(2);は、左から2枚目を99に入れ替える処理ですが、既にmyValue1(1);で1枚目が99に入れ替わっているため、下段は1枚目2枚目共に裏返して表示されます。

 3枚目は入れ替えがないためランダムですが、上段も下段も同じ数字が表示されます。これは、クロージャとしてvar myValue1 = func1(3);が実行された時の状態を保持しているためです。

 又、var myValue2 = func1(5);を実行すると、5枚のトランプを表示するクロージャを作成出来ます。

 このように、クロージャには以下のメリットがあると言えます。

・直前の状態を保持しながら、一部の状態を変化させる事が出来る
今回の例では、トランプの数字を保持しながら、一枚ずつ裏返しに出来ます。
・1つの関数から複数のクロージャを作成出来る
今回の例では、トランプを3枚表示する、5枚表示するといった複数のクロージャが簡単に作成出来ます。

無名関数での記述

 クロージャは、無名関数でも利用出来ます。最初に説明したクロージャの例を無名関数で書き直すと、以下になります。

【無名関数によるクロージャの利用】
<script>
var z = function(x) {
    return function(y) {
        return x + y;
    }
}

var myValue1 = z(10);
document.write(myValue1(5));
</script>

 赤字部分で無名関数を使っています。このため、青字部分はmyValue1に変数zを代入しています。

 尚、無名関数については、「関数の使い方」をご参照下さい。

次のページプロトタイプと継承

  • このエントリーをはてなブックマークに追加